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

SpiNNakerManchester / JavaSpiNNaker / 15136401884

20 May 2025 11:32AM UTC coverage: 37.528% (-0.8%) from 38.278%
15136401884

Pull #1227

github

rowleya
Make sure all errors are caught
Pull Request #1227: Emergency stop

114 of 152 new or added lines in 10 files covered. (75.0%)

243 existing lines in 6 files now uncovered.

9062 of 24147 relevant lines covered (37.53%)

1.12 hits per line

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

7.5
/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.util.Arrays.stream;
25
import static java.util.stream.Collectors.toList;
26
import static org.apache.commons.io.IOUtils.buffer;
27
import static org.slf4j.LoggerFactory.getLogger;
28
import static uk.ac.manchester.spinnaker.alloc.bmp.DataSectorTypes.BITFILE;
29
import static uk.ac.manchester.spinnaker.alloc.bmp.DataSectorTypes.REGISTER;
30
import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE;
31
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_ALL;
32
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_E_S;
33
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_N_NE;
34
import static uk.ac.manchester.spinnaker.messages.model.FPGA.FPGA_SW_W;
35
import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.LEDO;
36
import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.SCRM;
37
import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.SLEN;
38
import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice;
39
import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC;
40

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

54
import javax.annotation.PostConstruct;
55

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

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

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

81
        private static final int FLASH_DATA_LENGTH = 4096;
82

83
        private static final int CRC_OFFSET = FLASH_DATA_LENGTH - WORD_SIZE;
84

85
        private static final int CRC_BUFFER_LENGTH = 8192;
86

87
        private static final long CRC_MASK = 0xffffffffL;
88

UNCOV
89
        private static final MemoryLocation FLASH_DATA_ADDRESS =
×
90
                        new MemoryLocation(0x1000);
91

UNCOV
92
        private static final MemoryLocation BITFILE_BASE =
×
93
                        new MemoryLocation(0x200000);
94

95
        private static final int BITFILE_MAX_SIZE = 0x180000;
96

97
        private static final int DATA_SECTOR_CHUNK_SIZE = 128;
98

99
        private static final int NUM_DATA_SECTORS = 16;
100

101
        private static final int FPGA_ID_MASK = 0b00000011;
102

103
        private static final int CHIP_MASK = 0b111;
104

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

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

118
        private static final int DATA_SECTOR_LENGTH = 128;
119

120
        private static final int DATA_SECTOR_HEADER_WORDS = 8;
121

122
        private static final int DATA_SECTOR_HEADER_BYTES =
123
                        DATA_SECTOR_HEADER_WORDS * WORD_SIZE;
124

125
        private static final int BITFILE_NAME_MAX_LENGTH = 96;
126

127
        private static final int BITFILE_ENABLED_FLAG = 0x8000;
128

129
        private static final int PAD = 0xffffffff;
130

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

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

UNCOV
137
        private static final String[] CHIP_LABELS = {
×
138
                "", "0", "1", "10", "2", "20", "21", "210"
139
        };
140

UNCOV
141
        private static final String[] SLOT_LABELS = {
×
142
                "", "", "S0", "S1", "S2", "S3", "", "", "", "", "", "", "", "", "", ""
143
        };
144

145
        private static final double SMALL_SLEEP = 0.25;
146

147
        private static final double BIG_SLEEP = 12.0;
148

149
        private final BMPBoard board;
150

151
        private final BMPTransceiverInterface txrx;
152

153
        @Autowired
154
        private FirmwareDefinition firmware;
155

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

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

173
                FirmwareLoaderException(String msg) {
174
                        super(msg);
×
175
                }
×
176
        }
177

178
        /** An update of the firmware on a BMP failed. */
179
        public static class UpdateFailedException extends FirmwareLoaderException {
180
                private static final long serialVersionUID = 7925582707336953554L;
181

182
                /** The data read back from the BMP. */
183
                public final ByteBuffer data;
184

185
                UpdateFailedException(ByteBuffer data) {
186
                        super("failed to update flash data correctly!");
×
187
                        this.data = data.asReadOnlyBuffer();
×
188
                        this.data.order(LITTLE_ENDIAN);
×
189
                }
×
190
        }
191

192
        /** A CRC check failed. */
193
        public static class CRCFailedException extends FirmwareLoaderException {
194
                private static final long serialVersionUID = -4111893327837084643L;
195

196
                /** The CRC calculated by the BMP. */
197
                public final int crc;
198

199
                CRCFailedException(int crc) {
200
                        super(format("CRC by BMP failed to match: 0x%08x", crc));
×
201
                        this.crc = crc;
×
202
                }
×
203
        }
204

205
        /** A data chunk was too large for the firmware loader to handle. */
206
        public static class TooLargeException extends FirmwareLoaderException {
207
                private static final long serialVersionUID = -9025065456329109710L;
208

209
                TooLargeException(long size) {
210
                        super(format("Bit file is too large for BMP buffer: 0x%08x", size));
×
211
                }
×
212
        }
213

214
        private static final int SUB_WORD_MASK = 3;
215

216
        private static boolean notAligned(int offset) {
UNCOV
217
                return (offset & SUB_WORD_MASK) != 0;
×
218
        }
219

220
        private static class FlashDataSector {
221
                final ByteBuffer buf;
222

UNCOV
223
                FlashDataSector() {
×
UNCOV
224
                        buf = ByteBuffer.allocate(DATA_SECTOR_LENGTH);
×
UNCOV
225
                        buf.order(LITTLE_ENDIAN);
×
UNCOV
226
                }
×
227

228
                static FlashDataSector registers(int numItems, List<Integer> data) {
UNCOV
229
                        var fds = new FlashDataSector();
×
UNCOV
230
                        fds.registersHeader(numItems);
×
UNCOV
231
                        fds.registersPayload(data);
×
UNCOV
232
                        fds.buf.flip();
×
UNCOV
233
                        return fds;
×
234
                }
235

236
                static FlashDataSector bitfile(String name, int mtime, int crc,
237
                                FPGA chip, int timestamp, MemoryLocation baseAddress,
238
                                int length) {
UNCOV
239
                        var fds = new FlashDataSector();
×
UNCOV
240
                        fds.bitfileHeader(mtime, crc, chip, timestamp, baseAddress, length);
×
UNCOV
241
                        fds.bitfileName(name);
×
UNCOV
242
                        fds.buf.flip();
×
UNCOV
243
                        return fds;
×
244
                }
245

246
                private void pad(int targetLength, int value) {
UNCOV
247
                        while (buf.position() < targetLength
×
UNCOV
248
                                        && notAligned(buf.position())) {
×
UNCOV
249
                                buf.put((byte) value);
×
250
                        }
UNCOV
251
                        while (buf.position() < targetLength) {
×
UNCOV
252
                                buf.putInt(value);
×
253
                        }
UNCOV
254
                }
×
255

256
                private void registersHeader(int numItems) {
UNCOV
257
                        buf.put(REGISTER.value);
×
UNCOV
258
                        buf.put((byte) numItems);
×
UNCOV
259
                        pad(DATA_SECTOR_HEADER_BYTES, 0);
×
UNCOV
260
                }
×
261

262
                private void registersPayload(List<Integer> data) {
UNCOV
263
                        for (int item : data) {
×
UNCOV
264
                                buf.putInt(item);
×
UNCOV
265
                        }
×
UNCOV
266
                        pad(buf.capacity(), PAD);
×
UNCOV
267
                }
×
268

269
                private void bitfileHeader(int mtime, int crc, FPGA chip, int timestamp,
270
                                MemoryLocation baseAddress, int length) {
UNCOV
271
                        buf.put(BITFILE.value);
×
272

UNCOV
273
                        buf.put((byte) 0);
×
UNCOV
274
                        buf.putShort((short) (BITFILE_ENABLED_FLAG + chip.bits));
×
UNCOV
275
                        buf.putInt(timestamp);
×
UNCOV
276
                        buf.putInt(crc);
×
UNCOV
277
                        buf.putInt(baseAddress.address);
×
UNCOV
278
                        buf.putInt(length);
×
UNCOV
279
                        buf.putInt(mtime);
×
280

UNCOV
281
                        buf.putInt(PAD);
×
UNCOV
282
                        buf.putInt(PAD);
×
UNCOV
283
                }
×
284

285
                private void bitfileName(String name) {
UNCOV
286
                        var namedata = name.getBytes(StandardCharsets.UTF_8);
×
UNCOV
287
                        int namesize = min(namedata.length, BITFILE_NAME_MAX_LENGTH);
×
UNCOV
288
                        buf.put(1, (byte) namesize);
×
UNCOV
289
                        buf.put(namedata, 0, namesize);
×
UNCOV
290
                        while (buf.position() < buf.capacity()) {
×
UNCOV
291
                                buf.put((byte) 0);
×
292
                        }
UNCOV
293
                }
×
294
        }
295

296
        /**
297
         * Instructions to set a register on one or more FPGAs. This is done not
298
         * just immediately, but also during BMP boot.
299
         *
300
         * @author Donal Fellows
301
         */
302
        public static class RegisterSet {
303
                private final FPGA fpga;
304

305
                private final MemoryLocation address;
306

307
                private final int value;
308

309
                /**
310
                 * @param fpga
311
                 *            Which FPGA's registers to set
312
                 * @param register
313
                 *            Which register is this
314
                 * @param value
315
                 *            The value to set
316
                 */
UNCOV
317
                public RegisterSet(FPGA fpga, FPGAMainRegisters register, int value) {
×
UNCOV
318
                        this.fpga = fpga;
×
UNCOV
319
                        this.address = register.getAddress();
×
UNCOV
320
                        this.value = value;
×
UNCOV
321
                }
×
322

323
                /**
324
                 * @param fpga
325
                 *            Which FPGA's registers to set
326
                 * @param register
327
                 *            Which register is this
328
                 * @param bank
329
                 *            In which register bank (i.e., for which link)
330
                 * @param value
331
                 *            The value to set
332
                 */
333
                public RegisterSet(FPGA fpga, FPGALinkRegisters register, int bank,
334
                                int value) {
×
335
                        this.fpga = fpga;
×
336
                        this.address = register.address(bank);
×
337
                        this.value = 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
UNCOV
344
                var slice = target.duplicate();
×
UNCOV
345
                slice.position(offset);
×
UNCOV
346
                slice.put(source);
×
UNCOV
347
        }
×
348

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

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

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

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

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

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

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

410
        /**
411
         * The read part of {@code cmd_xreg} and {@code cmd_xboot} from
412
         * {@code bmpc}. This merges the two because there's little point in keeping
413
         * them separate for our use case and that avoids doing some network
414
         * traffic.
415
         */
416
        private void listFPGABootChunks()
417
                        throws ProcessException, IOException, InterruptedException {
UNCOV
418
                var data = readFlashDataHead();
×
UNCOV
419
                for (int i = 0; i < NUM_DATA_SECTORS; i++) {
×
UNCOV
420
                        var chunk = slice(data, DATA_SECTOR_CHUNK_SIZE * i,
×
421
                                        DATA_SECTOR_CHUNK_SIZE);
UNCOV
422
                        byte type = chunk.get();
×
UNCOV
423
                        switch (DataSectorTypes.get(type)) {
×
424
                        case REGISTER:
UNCOV
425
                                logRegisterSets(chunk);
×
UNCOV
426
                                break;
×
427
                        case BITFILE:
UNCOV
428
                                logFPGABootBitfile(chunk, i);
×
UNCOV
429
                                break;
×
430
                        default:
431
                                // Ignore the chunk
432
                                break;
433
                        }
434
                }
UNCOV
435
        }
×
436

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

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

UNCOV
478
                var state = (flags & BITFILE_ENABLED_FLAG) > 0 ? "ENABLED "
×
UNCOV
479
                                : "DISABLED";
×
UNCOV
480
                log.info("FPGA BOOT:      {}", format(
×
481
                                "%3s  %s  Chips %-3s, Base 0x%06x, Length %8d, CRC 0x%08x",
UNCOV
482
                                SLOT_LABELS[i], state, CHIP_LABELS[flags & CHIP_MASK], base,
×
UNCOV
483
                                length, crc));
×
UNCOV
484
                log.info("FPGA BOOT:           File      {}",
×
UNCOV
485
                                new String(filenameBytes, 0, size, UTF_8).strip());
×
UNCOV
486
                log.info("FPGA BOOT:           Written   {}",
×
UNCOV
487
                                Instant.ofEpochSecond(time));
×
UNCOV
488
                log.info("FPGA BOOT:           ModTime   {}",
×
UNCOV
489
                                Instant.ofEpochSecond(mtime));
×
UNCOV
490
        }
×
491

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

UNCOV
516
                var flashData = readFlashData();
×
UNCOV
517
                putBuffer(flashData, sector.buf, REGISTER_DATA_SECTOR_LOCATION);
×
UNCOV
518
                updateFlashData(flashData);
×
UNCOV
519
        }
×
520

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

UNCOV
553
                if (size > BITFILE_MAX_SIZE) {
×
554
                        throw new TooLargeException(size);
×
555
                }
556

UNCOV
557
                var base = BITFILE_BASE.add(slot * BITFILE_MAX_SIZE);
×
UNCOV
558
                try (var s = buffer(resource.getInputStream())) {
×
UNCOV
559
                        txrx.writeSerialFlash(board, base, size, s);
×
560
                }
UNCOV
561
                int otherCRC = txrx.readSerialFlashCRC(board, base, size);
×
UNCOV
562
                if (otherCRC != crc) {
×
563
                        throw new CRCFailedException(otherCRC);
×
564
                }
565

UNCOV
566
                int timestamp = (int) (currentTimeMillis() / MSEC_PER_SEC);
×
567

UNCOV
568
                var sector = FlashDataSector.bitfile(name, mtime, crc, chip,
×
569
                                timestamp, base, size);
570

UNCOV
571
                var flashData = readFlashData();
×
UNCOV
572
                putBuffer(flashData, sector.buf, BITFILE_DATA_SECTOR_LOCATION);
×
UNCOV
573
                updateFlashData(flashData);
×
UNCOV
574
        }
×
575

576
        private void sleep(double secs) throws InterruptedException {
UNCOV
577
                Thread.sleep((long) (secs * MSEC_PER_SEC));
×
UNCOV
578
        }
×
579

580
        /**
581
         * Load the FPGA definitions.
582
         *
583
         * @param postDelay
584
         *            Whether to do the long delay after the FPGA boot/check
585
         * @throws InterruptedException
586
         *             If interrupted while sleeping
587
         * @throws ProcessException
588
         *             If a BMP rejects a message
589
         * @throws IOException
590
         *             If the network fails or the packaged bitfiles are unreadable
591
         * @throws FirmwareLoaderException
592
         *             If something goes wrong.
593
         */
594
        public void bitLoad(boolean postDelay)
595
                        throws InterruptedException, ProcessException, IOException {
596
                // Bleah
UNCOV
597
                int idx = 0;
×
598

UNCOV
599
                setupRegisters(new RegisterSet(FPGA_ALL, SCRM, CLEAR),
×
600
                                new RegisterSet(FPGA_ALL, SLEN, SET),
601
                                new RegisterSet(FPGA_ALL, LEDO, CLEAR));
UNCOV
602
                sleep(SMALL_SLEEP);
×
603

UNCOV
604
                var nameDef = firmware.bitfileNames.get(idx);
×
UNCOV
605
                setupBitfile(nameDef, 0, FPGA_ALL);
×
UNCOV
606
                idx++;
×
607

UNCOV
608
                sleep(SMALL_SLEEP);
×
609

UNCOV
610
                setupBitfile(firmware.bitfileNames.get(idx), idx, FPGA_E_S);
×
UNCOV
611
                idx++;
×
UNCOV
612
                setupBitfile(firmware.bitfileNames.get(idx), idx, FPGA_SW_W);
×
UNCOV
613
                idx++;
×
UNCOV
614
                setupBitfile(firmware.bitfileNames.get(idx), idx, FPGA_N_NE);
×
615

616
                // TODO these read the configuration... but are they necessary?
UNCOV
617
                listFPGABootChunks();
×
UNCOV
618
                logBMPVersion();
×
619

UNCOV
620
                if (postDelay) {
×
UNCOV
621
                        log.info("beginning post-load delay");
×
UNCOV
622
                        sleep(BIG_SLEEP);
×
UNCOV
623
                        log.info("completed post-load delay");
×
624
                }
UNCOV
625
        }
×
626
}
627

UNCOV
628
enum DataSectorTypes {
×
629
        /** Chunk describes a bitfile to load. */
UNCOV
630
        BITFILE(3),
×
631
        /** Chunk describes some registers to set. */
UNCOV
632
        REGISTER(4),
×
633
        /** Chunk is not recognised. */
UNCOV
634
        @Deprecated
×
635
        UNKNOWN(-1);
636

637
        /** The value of the chunk's type code. */
638
        final byte value;
639

UNCOV
640
        DataSectorTypes(int value) {
×
UNCOV
641
                this.value = (byte) value;
×
UNCOV
642
        }
×
643

644
        static DataSectorTypes get(byte val) {
UNCOV
645
                if (val == BITFILE.value) {
×
UNCOV
646
                        return BITFILE;
×
UNCOV
647
                } else if (val == REGISTER.value) {
×
UNCOV
648
                        return REGISTER;
×
649
                } else {
UNCOV
650
                        return UNKNOWN;
×
651
                }
652
        }
653
}
654

655
@Component
656
class FirmwareDefinition {
3✔
657
        private static final Logger log = getLogger(FirmwareDefinition.class);
3✔
658

659
        @Value("classpath:bitfiles/manifest.properties")
660
        private Resource manifestLocation;
661

662
        /** The <em>ordered</em> list of firmware filenames from the manifest. */
663
        List<String> bitfileNames;
664

665
        /** Where to load each of the bitfiles from. */
666
        private final Map<String, Resource> bitFiles = new HashMap<>();
3✔
667

668
        /**
669
         * What the intended modification time of each of the bitfiles is. From the
670
         * manifest, because actual file modification times are broken.
671
         */
672
        private final Map<String, Integer> modTimes = new HashMap<>();
3✔
673

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

698
        Resource resource(String handle) {
UNCOV
699
                return bitFiles.get(handle);
×
700
        }
701

702
        int mtime(String handle) {
UNCOV
703
                return modTimes.get(handle);
×
704
        }
705
}
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