Bachelor’s Thesis

Bachelor’s Degree in Industrial Technology Engineering

Over-the-air FPGA programming using

Author: Alexandre Araiza i Tamarit Director: Juan Manuel Moreno Eguilaz Call: September 2020

Escola Tècnica Superior d’Enginyeria Industrial de Barcelona

Acknowledgements

First and foremost, I would like to thank my thesis director Juan Manuel Moreno Eguilaz for introducing me to the world of FPGAs and for helping me out along the way. I would also like to thank my parents for providing feedback about their areas of expertise and for their support during this project. Last but not least, I would like to thank the people at MoCo Makers for brin- ging FPGAs to a wider audience. If it weren’t for the Fipsy FPGA, this project wouldn’t have been possible.

Abstract

Field-programmable gate arrays are slowly becoming popular for many different applications, securing their place in the future of electronics. This project aims to expand on the current state of field-programmable gate array development by providing a tool that enables remote development of a commercially available field-programmable gate array using Wi-Fi, the most popular wireless networking technology. A web-based tool is developed as a result, enabling multiple users to manipulate the field-programmable gate array without the need for additional software.

Table of contents

Abstract...... 5

Glossary...... 9

1. Introduction...... 11

2. Theoretical foundations...... 13 2.1. Data transmission...... 13 2.1.1. Serial Peripheral Interface...... 13 2.1.2. Wi-Fi...... 15 2.2. Hardware...... 15 2.2.1. Field-programmable gate array...... 15 2.2.2. WeMos D1 R1...... 20 2.3. Software...... 21 2.3.1. Arduino IDE...... 21 2.3.2. Lattice Diamond...... 23

3. FPGA programming...... 25 3.1. FPGA Commands...... 26 3.2. User commands ...... 28 3.3. JEDEC file...... 30

4. Wi-Fi implementation...... 33 4.1. Hypertext Transfer Protocol...... 35 4.1.1. HTTP request...... 36 4.1.2. HTTP response...... 38 4.2. Basic request handlers...... 39 4.3. File system...... 40 4.4. File system request handlers...... 41 4.5. Tool usability...... 42

5. User interface...... 43 5.1. User commands...... 44 5.2. Status messages...... 45 5.3. Managing the file list...... 45 5.4. Checking JEDEC files...... 46 5.5. Web page responsiveness...... 46

6. Future updates...... 49

7. Resource use...... 51 7.1. Hardware...... 51 7.2. Software...... 51 7.3. Time and human resources...... 52 7.4. Financial resources...... 53 7.5. Environmental resources...... 54

Conclusions...... 55

Bibliography...... 57

Appendices...... 59 Appendix 1...... 61 Appendix 2...... 87 Appendix 3...... 91 Appendix 4...... 97 Appendix 5...... 105 9

Glossary

AJAX Asynchronous JavaScript and XML. Set of technologies to update web pages asynchronously.

IDE Integrated development environment. Software applica- tion providing tools for software development.

IEEE Institute of Electrical and Electronics Engineers. Profes- sional association for electrical and electronic enginee- ring.

IP address Internet Protocol address. Numerical address assigned to each device using the Internet Protocol in a computer network.

IP core Intellectual property core. Reusable block of logic that is the intellectual property of an entity.

LAN Local Area Network. Computer network spanning a limi- ted area, such as a residence, school or office building.

LUT Lookup table. Array used to look up values usually diffi- cult to compute.

Request handler Program used to process incoming requests.

Robustness Ability of computer systems to deal with errors.

Runtime Time span of program execution on a computer.

Single-board Microcontroller built onto a printed circuit board.

11

1. Introduction

From the growing to the rise of artificial intelligence, field-pro- grammable gate arrays are slowly making their way into the future of electronics. Their versatility allows them to be used in various industries for many different applications, and their ever-decreasing price attracts more and more people to enter the world of field-programmable gate array development. Devices are getting smaller and more connected, some of them being so small that they can be difficult to handle, making development impractical. As lots of devices use a field-programmable gate array during development, it could be helpful to use some form of connectivity to manipulate it remotely. This is the main objective of this project; developing a tool to allow a commercially available field-programmable gate array to be programmed remotely. To achieve this goal, a wireless networking technology is necessary. Wi-Fi will be used, as it is the most common technology for wireless local area networks. A single-board microcontroller will also be used to communicate with the field-pro- grammable gate array and provide Wi-Fi functionality. Software optimization will be taken into account to reduce runtime and improve robustness, but ease of use will be the top priority, as that in itself can reduce the amount of time it takes the user to learn how to use the tool as well as do different actions with it, making the user experience smoother. The final product will be the result of modifying existing source code to make use of Wi-Fi functionality on an Arduino compatible single-board microcontroller.

13

2. Theoretical foundations

2.1. Data transmission

Data transmission is a fundamental part of electronics, enabling devices to com- municate with each other and thus allowing data to be manipulated from different places. For devices to understand each other, data has to be sent in the same way it is received; the device sending data and the device receiving it need to use the same communication protocol. Two different communication protocols will be used in this project.- Thesin gle-board microcontroller will transfer data to the FPGA via SPI and the PC will transfer data to the single-board microcontroller via Wi-Fi.

2.1.1. Serial Peripheral Interface

Serial Peripheral Interface (SPI) is a synchronous serial communication interface, which enables data transfer between a master device and slave devices. It includes at least four lines: [1]

• Serial Clock (SCLK) This line carries the clock signal, which is outputted from the master device. • Master Output Slave Input (MOSI) This line carries the data sent from the master device to the slave device. • Master Input Slave Output (MISO) This line carries the data sent from the slave device to the master device. 14 Over-the-air FPGA programming using Arduino

• Slave Select (SS) This line carries a signal which is active when the master device commu- nicates with the selected slave device and inactive otherwise.

There needs to be one slave select line for each slave device with which the master device communicates. There are ways to avoid this, such as daisy-chaining, but they have other disadvantages.

Figure 1. SPI wiring

The master selects the slave with which it will communicate by driving the co- rresponding slave select line to its active state. It then outputs a clock signal to the SCLK line to begin data transmission. On each clock cycle, the master sends one bit to the slave via the MOSI line, and the slave sends one bit to the master via the MISO line. These bits are usually sent to an 8-bit shift register in both devices, with the most significant bit first, each bit being set as the new least sig- nificant bit of the shift register. There are different ways to determine when a clock cycle begins and ends, as well as for the output and input sides of the transmission to send and receive data. In Theoretical foundations 15

the interface used for this project, the clock cycle begins and ends on the falling edge of the clock signal. The output side of the transmission changes its value on the falling edge of the clock signal, and the input side of the transmission captures that value on the rising edge of the clock signal. This is commonly known as mode 0. [2]

Figure 2. SPI mode 0

2.1.2. Wi-Fi

Wi-Fi is a trademark of Wi-Fi Alliance, a non-profit organization that certifies products for conforming to the IEEE 802.11 family of standards of interoperabi- lity. However, the term “Wi-Fi” is widely used to refer to the IEEE 802.11 family of standards itself. Since some of the IEEE 802.11 standards will be used in this project, they will be referred to as “Wi-Fi” for simplicity. The IEEE 802.11 family of standards consists of modulation techniques that enable wireless communication. They are the most popular wireless networking standards, mostly used for local area networks (LAN).

2.2. Hardware

2.2.1. Field-programmable gate array

A field-programmable gate array (FPGA) is an integrated circuit that canbe programmed to implement different electronic circuits. It usually consists of three major blocks: [3] 16 Over-the-air FPGA programming using Arduino

• Configurable Logic Blocks (CLB) Configurable logic blocks contain look-up tables, full adders, flip-flops, multiplexers, and other logic cells that are capable of implementing diffe- rent functions depending on their configuration. • Input/Output Blocks (I/O Blocks) Input/output blocks enable other devices to communicate with the FPGA. • Switch Matrices Switch matrices are made of transistors, which can connect lines carrying different signals through the FPGA.

Figure 3. Major FPGA blocks

The FPGA used in this project is the LCMXO2-256HC-4SG32C by Lattice Se- miconductor. It contains an array of 32 logic blocks, each called Programmable Functional Unit (PFU), surrounded by Programmable I/O Cells (PIC). It also contains on-chip flash memory and an Embedded Function Block (EFB). 4[ ] Theoretical foundations 17

Figure 4. LCMXO2-256HC-4SG32C architecture

Programmable Functional Unit Each PFU consists of 4 interconnected slices numbered 0 to 3. Each slice can be programmed to perform logic, arithmetic, distributed RAM and distributed ROM functions, and contains 2 4-input lookup tables (LUT4) and 2 registers, implemen- ted with flip-flops or latches.4 [ ]

Figure 5. Programmable Functional Unit 18 Over-the-air FPGA programming using Arduino

A LUT4 has 4 binary inputs, resulting in 16 possible input combinations, which can point to 16 possible outputs. 2 LUT4 can be combined into a LUT5 equivalent by using each LUT4 for 16 outputs, resulting in a total of 32 possible outputs. In the same manner, 4 LUT4 can be combined into a LUT6 equivalent, resulting in a total of 64 possible outputs, and 8 LUT4 can be combined into a LUT7 equiva- lent, resulting in a total of 128 possible outputs. This can be used when operating in logic mode, allowing for multiple slices in one PFU to be combined in order to implement up to LUT7 equivalents. 2 PFU can also be combined to implement a LUT8 equivalent. Since a LUT4 can store 16 1-bit values, 4 LUT4 can be combined to store 16 4-bit values. This is exactly what happens when slices 0, 1 and 2 operate in RAM mode. Slices 0 and 1 combine their LUT4 to store 4-bit values and slice 2 provides memory address and control signals. Slice 3 cannot operate in RAM mode. This leaves each PFU with a maximum RAM memory of 64 bits. Since the FPGA con- tains 32 PFU, its maximum RAM memory is 2048 bits (2 kb).

Programmable I/O Cell Each PIC contains 4 Programmable I/O (PIO), which consist of an input register block, an output register block and a tristate register block. These blocks contain the necessary circuitry to allow pins to operate in input or output modes safely. [4]

Figure 6. Programmable I/O Cell Theoretical foundations 19

Flash memory The flash memory stores the configuration data. There are 575 pages of flash- me mory available, each page holding 128 bits (16 bytes) of data. It can be accessed by the SPI port. [5] The flash memory has a sector for the feature row, which is used to control FPGA resources. In the case of the LCMXO2-256HC-4SG32C FPGA, it contains 16 bits, which represent the resources shown in the figure below.

Figure 7. LCMXO2-256HC-4SG32C Feature Row [Source: Lattice Diamond]

The feature row can be erased and programmed independently.

Embedded Function Block The EFB contains an SPI intellectual property core (IP core) that can be con- figured as a master or a slave. When configured as a slave, it can interface with external master devices. [4]

Routing The routing consists of switching circuitry, buffers and three types of metal inter- connect segments, which span 2 PFU, 3 PFU and 7 PFU. [4] 20 Over-the-air FPGA programming using Arduino

Fipsy FPGA To facilitate working with this FPGA, the Fipsy FPGA Breakout Board by MoCo Makers will be used. This breakout board includes 20 holes through which pin headers can be soldered to make a device that can be plugged into a breadboard or into female jump wires.

Figure 8. Fipsy FPGA Breakout Board

2.2.2. WeMos D1 R1

The WeMos D1 R1 is an Arduino compatible single-board microcontroller. The microcontroller it is based on is the ESP8266EX, produced by Espressif Systems. This microcontroller offers Wi-Fi networking capabilities, with 802.11 b/g/n su- pport in the 2.4 GHz frequency band, as well as SPI. [6] Theoretical foundations 21

Figure 9. WeMos D1 R1

The board contains a CH340G integrated circuit, which converts USB to serial interface and vice versa, allowing the microcontroller to communicate with an external device via the USB Micro B port. The board can be powered using the USB Micro B port or the DC power connector.

2.3. Software

2.3.1. Arduino IDE

The Arduino Integrated Development Environment (IDE) is an application used to write and upload programs to Arduino compatible boards. It runs on Windows, Mac OS X and Linux. It supports working with .ino files, called sketches, which are written in Arduino programming language. The Arduino programming language is very similar to C++, with their data types and structure being almost identical. 22 Over-the-air FPGA programming using Arduino

Source code written in the Arduino IDE requires two functions: [7]

• setup(), which runs once at the start of the execution • loop(), which runs consecutively

Figure 10. Arduino IDE

The setup function is typically used to initialize variables, pin modes, communi- cation ports and anything else required for the loop function to execute properly. The loop function contains all the code that will be run during normal operation.

Serial port The Arduino programming language includes a built-in object called Serial, which enables the microcontroller to communicate via the serial port. It defines the fo- llowing methods, among others: [7]

• begin(), which initializes the serial port • print(), which prints data to the serial port as ASCII characters • println(), which prints data to the serial port as ASCII characters, fo- llowed by a carriage return character and a newline character • read(), which reads the first byte of incoming serial data • readStringUntil(), which reads bytes of incoming serial data into a string until a terminator character is reached Theoretical foundations 23

SPI library The SPI library is a standard Arduino library which enables the microcontroller to communicate using SPI as the master device. It defines the following methods, among others: [7]

• begin(), which initializes the SPI port • beginTransaction(), which starts an SPI transaction • endTransaction(), which ends the SPI transaction • transfer(), which transfers data over the SPI bus

2.3.2. Lattice Diamond

Lattice Diamond is an FPGA design tool. It allows the creation of projects, to which files can be added to specify the behavior of the FPGA once it is program- med. It supports VHDL and Verilog/SystemVerilog hardware description languages as well as various constraint file formats. It includes a tool to verify the functionality of the design via simulation, a synthesis tool and several other tools to optimize the design, such as place and route, timing analysis or power calculation.

Figure 11. Lattice Diamond

It can export the programming file as a bitstream or as a JEDEC file.

25

3. FPGA programming

MoCo Makers published files on their GitHub profile that allow the Fipsy FPGA to be programmed via an Arduino board connected to a computer. These files are Arduino_Programmer.ino along with the header file MachXO2.h, which are to be uploaded to the microcontroller, and FileChooser.py, which is to be run on the computer. [8] Once the user runs FileChooser.py, a window appears, letting the user check the device’s ID, erase the device, or program the device with a chosen file. Once the user runs a command, a string is sent through the selected USB port to the mi- crocontroller, which reads it and executes the corresponding block of code, inte- racting with the Fipsy FPGA via SPI.

Figure 12. Arduino JEDEC Programmer - MoCo Makers 26 Over-the-air FPGA programming using Arduino

Figure 13. Original network

3.1. FPGA Commands

For the Fipsy FPGA to perform the desired actions, the right commands need to be sent to it. These are specified in Table 22 of the MachXO2 Programming and Configuration Usage Guide, from which the ones used in Arduino_Programmer. ino are listed below:

Table 1. FPGA Commands [5]

Command Command Operands Write Data Read Data Notes Name Read Device 0xE0 00 00 00 N/A YY YY YY Characters ID YY represent the device-specific ID code Enable 0xC6 08 00 00 N/A N/A Enable the Configuration Configuration Interface Logic for (Offline device Mode) programming in Offline mode Read Busy 0xF0 00 00 00 N/A YY Bit 7 Flag 1 = Busy 0 = Ready Erase 0x0E 0F 00 00 N/A N/A Operand 0F erases SRAM, Feature Row and Configuration Flash FPGA programming 27

Command Command Operands Write Data Read Data Notes Name Reset 0x46 00 00 00 N/A N/A Set Page Configuration Address Flash Address pointer to the beginning of the Configuration Flash sector Program Page 0x70 00 00 01 YY * 16 N/A Program one Flash page Write Feature 0xE4 00 00 00 YY * 8 N/A Program the Row Feature Row bits Write 0xF8 00 00 00 YY * 2 N/A Program the FEABITS FEABITS Program 0x5E 00 00 00 N/A N/A Program the DONE DONE status bit enabling SDM Bypass 0xFF FF FF FF N/A N/A No Operation and Device Wakeup Refresh 0x79 00 00 N/A N/A Force the FPGA to reconfigure

These commands are read by the FPGA, along with the operands and any write data. The FPGA then performs the action corresponding to the received com- mand and returns the read data, if any. 28 Over-the-air FPGA programming using Arduino

3.2. User commands

Check device ID Check device ID gets the identification number from the device connected to the board. This action can be used to check whether the SPI communication between the device and the microcontroller is wor- king properly or not. It may also be used to check which device is currently connected, although it may not be useful in some cases, as different devices can have the same identification number. In Arduino_Programmer.ino, when the microcon- troller receives a “do_check_id” string, it sends a “bypass” command to wake up the device, followed by a “read device ID” command. It then makes a copy of the received data to an address in memory and prints it to the serial port. [8]

Erase device Erase device clears the configuration of the device connected to the board. This action returns the de- vice to its erased state. In Arduino_Programmer.ino, when the microcon- troller receives a “do_erase_device” string, it sends an “enable configuration interface (offline mode)” command. Once the configuration logic is enabled, it sends an “erase” command. After that, it waits 100 ms, then sends a ”read busy flag” command. If the FPGA is busy, it waits 100 ms and sends a “read busy flag” again until the FPGA is ready or until a timeout of 5000 ms is reached. [8] FPGA programming 29

Program device Program device writes part of a JEDEC file to the flash memory of the device connected to the board. This, in turn, sets the corresponding switch matri- ces to the state described by the JEDEC file. Since this action requires an external file, a way to pass this file along with the command to program the device is needed. In Arduino_Programmer.ino, when the micro- controller receives a “do_program_device” string, it erases the device, performing the same actions described in the previous section. If the device is erased successfully, the microcontroller then per- forms a check of the JEDEC file. If this check is successful, it then proceeds to program the FPGA. To do this, it first sends a “reset configuration flash address” command. Then, for every page until the end of the configuration data, it sends a “program page” command, with that page’s bytes as the write data. Once the configuration data is programmed, it sends a “write feature row” command, with the feature row bytes as the write data, then a “write feabits” command, with the feabits as the write data. Finally, it sends a “program done” command, followed by a “refresh” command and another “pro- gram done” command. [8] 30 Over-the-air FPGA programming using Arduino

3.3. JEDEC file

JEDEC files are used to program the FPGA. The format for a JEDEC file desig- ned to program the LCMXO2-256HC-4SG32C FPGA has the following structure:

Table 2. JEDEC File Format [5]

JEDEC Syntax Description Field Start-of-text 0x02 0x02 marks the beginning of the JEDEC file. Only ASCII characters are legal after it. Field * Each field in the JEDEC file is terminated with an asterisk. Terminator Note NOTE ... The keyword N marks the beginning of the comment. It can (Comment) appear anywhere in the JEDEC file. Lattice’s JEDEC files add “OTE” to the N keyword to make it the more meaningful word NOTE. Fuse Count QF73600 The keyword QF identifies the total real fuse count of the device. OTP and G0 G<0 = none, 1 = only security, 2 = only OTP, 3 = both>. Security Setting Default Fuse F0 The keyword F identifies the fuse state of those fuses not State included in the link field. F0 = fill them with zeros (0), F1 = fill them with ones (1). It is defined for the purpose of reducing JEDEC file size. It has no meaning in Lattice’s JEDEC file. Lattice recommends using compression to reduce file size instead. FPGA programming 31

JEDEC Syntax Description Field Link Field L00000 The keyword L identifies the first fuse address of the fuse 1… pattern that follows after the white space. The number of ...1 digits shown following the L keyword must be the same as * that on the QF field. In this example, QF73600 has five digits, thus L00000 should have five zeros. The fuse address traditionally starts counting from 0. The link field is the most critical portion of the JEDEC file where the programming pattern is stored. The programming data is written into this field in the manner mirroring exactly the fuse array layout of the silicon physically. The row address is written from top to bottom in ascending order: top = row 0, bottom = last row. The column address is written from left to right in ascending order: leftmost = bit 0, rightmost = last bit. The end of the Configuration Flash data is marked by “NOTE END CONFIG DATA*”. It is not necessary to program any page data containing all ‘0’ values. Fuse ABCDE The checksum of all the fuses = Fuse count. The fuse state Checksum of all the fuses can be found from the Link field. If it is not specified in the link field, then use the Default Fuse State in their places. E Field E0...0 JEDEC standard defines this field to hold the architecture 0...0* fuses. Lattice uses this field to store the Feature Row and FEABITS. The Feature Row data is on the first line. The FEABITS values are on line 2. U Field U0...0 This is the place to store the 32-bit USERCODE. The 32-bit USERCODE can be expressed in UA = ASCII, UH = ASCII Hex, U = Binary. End-of-text 0x03 0x03 marks the ending of the JEDEC file. Transmission ABCD This is the checksum of the whole file starting from 0x02 to Checksum 0x03. All characters and white space, including the 0x02 and 0x03, are included in the checksum calculation.

33

4. Wi-Fi implementation

The ESP8266EX microcontroller will provide Wi-Fi functionality in this project. There is documentation detailing the use of multiple libraries written for ESP8266 . To implement Wi-Fi functionality, the ESP8266WiFi library will be used. It allows the ESP8266 to operate in station mode, in soft access point mode, as a client and as a server. [9]

Station mode Station mode allows the ESP8266 to connect to a LAN established by an access point.

Figure 17. ESP8266 operating in station mode

Soft access point mode Soft access point mode allows the ESP8266 to function as a wireless access point, creating its own LAN to which up to 8 devices can be connected.

Figure 18. ESP8266 operating in soft access point mode 34 Over-the-air FPGA programming using Arduino

The fact that the ESP8266 can operate as a server is particularly useful, since it enables it to handle incoming requests (sent to its IP address) and execute different blocks of code accordingly. Furthermore, all clients in the same LAN as the server can communicate with it, meaning that the FPGA programming tool could be used by multiple devices. To take full advantage of this, a tool capable of running on a web browser will be developed, removing the need for additional software and thus allowing any new devices capable of browsing the web to use the tool as soon as they join the LAN. The system networks with the ESP8266 operating in station mode and soft access point mode will be the following, respectively:

Figure 19. Station mode network Wi-Fi implementation 35

Figure 20. Soft access point mode network

To develop a tool capable of running on a web browser, Hypertext Transfer Pro- tocol (HTTP) will be used, since it is a standard for web information transfer and the protocol to which web browsers default when no protocol is specified in the address bar. The WiFiServer class defined in the ESP8266WiFi library is very simple to use, but doesn’t include methods to handle HTTP communication. Thankfully, there is a dedicated library for ESP8266 microcontrollers to operate as web servers; ESP8266WebServer, which does support HTTP communication. This library su- pports only one request at a time, but since the FPGA programming tool will most likely only be used by one person at a time, this should not be a problem. [10]

4.1. Hypertext Transfer Protocol

HTTP is a request-response protocol used by the client-server model. Client sof- tware running on a device requests data from server software, typically running on another device, and the server responds. This pattern is synchronous; the client holds the connection open and waits until it receives a response or until the con- 36 Over-the-air FPGA programming using Arduino

nection times out. [11] Requests and responses are sent through HTTP messages, which have the fo- llowing structure:

1. Start-line 2. Headers (optional) 3. Blank line 4. Body (optional)

The start-line and headers are the head of the message, and the body is the payload of the message. The blank line acts as a delimiter between the head and the payload. The start-line contains information about the message and the version of HTTP it uses. Headers pass additional information with the HTTP message. They consist of key-value pairs and can be classified as general, request, response or entity hea- ders. General headers can be used in requests and responses, without relation to the body. An example of a general header is Connection, which controls the status of the connection after the current HTTP message is delivered. Its value is usually either close or keep-alive. Request and response headers can be used exclusively in requests and responses, respectively. Entity headers can also be used in requests and responses, but contain informa- tion about the body. Examples of entity headers are Content-Length and Con- tent-Type, which contain the length of the body in bytes and the media type of the body, respectively.

4.1.1. HTTP request

Start-line The start-line of an HTTP request contains the method, the request target and the HTTP version. Wi-Fi implementation 37

The method indicates the action to perform on the request target. GET and POST are the only methods supported by the ESP8266WebServer library, and they are usually used to retrieve and submit data respectively. The request target is usually a Uniform Resource Locator (URL) or a path. A URL contains all the information required to retrieve information from the web. It has the following structure:

scheme : // authority path ? query # fragment

The scheme usually indicates the protocol used to communicate; the authority usually consists of only the host, either as a hostname or as an IP address; the path consists of path segments separated by a slash; the query consists of a string which assigns values to parameters; and the fragment consists of a string of cha- racters referring to a resource. Only the scheme, the colon and the path are man- datory; the rest are optional. Since all HTTP requests will be sent to the ESP8266 server, whose IP address may change, a path will be used as the request target. HTTP/1.1 is the only version supported by the ESP8266WebServer library.

Headers HTTP request headers can include general, request and entity headers. Examples of request headers are Accept, Accept-Encoding, Accept-Language, which specify which content types, encodings and languages the client can understand, respec- tively; Host, which specifies where the request is sent; Referer, which specifies the address of the web page from which a link to the requested web page was followed; and User-Agent, which identifies the user agent making the request, usually a web browser.

Body GET requests usually don’t need a body, since they are often used to fetch data. POST requests are often used to send data to the server, so they usually need a body. 38 Over-the-air FPGA programming using Arduino

4.1.2. HTTP response

Start-line The start-line of an HTTP response is called the status-line, and it contains the HTTP version, the status code and the status text. There are five classes of HTTP responses, each with their own status code range:

1. Informational response (status codes 100 to 199) 2. Successful response (status codes 200 to 299) 3. Redirect (status codes 300 to 399) 4. Client error (status codes 400 to 499) 5. Server error (status codes 500 to 599)

Some of the most common HTTP response codes and texts are the following:

Table 3. Common HTTP response codes and texts

Status code and text Description 200 OK the request has succeeded 201 Created the request has succeeded and a resource has been created 403 Forbidden the client doesn’t have access to the content 404 Not Found the server can’t find the requested resource 500 Internal Server Error the server had an error it couldn’t handle

Headers HTTP response headers can include general, response and entity headers.

Body HTTP responses may or may not include a body: responses with a status code that answers the request without the need for additional data usually don’t inclu- de a body. Wi-Fi implementation 39

4.2. Basic request handlers

The ESP8266WebServer library supports client request handlers, allowing diffe- rent functions to be executed when different URL paths are accessed. This allows different paths to be used to run different user commands, such as “check device ID”, “erase device” and “program device”. This way, when the client accesses the path corresponding to a particular command, the server will execute the block of code corresponding to that command, and the result of the execution can be sent as a response payload.

Check device ID When the user accesses the path “/id”, an HTTP GET request is sent to that path, triggering the execution of the “check device ID” command. The ID of the device is then sent as the payload of a response with status code 200.

Erase device When the user accesses the path “/erase”, an HTTP GET request is sent to that path, triggering the execution of the “erase device” command. The result of that execution is then sent as the payload of a response. If the device is erased success- fully, a response with status code 200 is sent. If not, a response with status code 500 is sent. This is not the usual way for an HTTP GET request to be processed, but it is better than sending an HTTP POST request, since no data needs to be sent to the server.

Program device When the user accesses the path “/program”, a request is sent to that path, trig- gering the execution of the “program device” command. For the FPGA to be programmed, a JEDEC file needs to be passed along with the command to program the device. This could be done with an HTTP POST request, sending the file as the body of the request. However, this has a draw- back: the connection between the client and the server would have to stay open during programming, since chunks of the JEDEC file would need to be sent to 40 Over-the-air FPGA programming using Arduino

the ESP8266 as the FPGA gets programmed. Thus, if the connection were to fail during programming, the FPGA would be left in a partially programmed state, unable to continue. This could be prevented by storing the JEDEC file in the server’s memory. By doing so, a “program device” command could start the programming sequence, and a connection failure would not affect the execution, as the server could conti- nue reading the file from its own memory. Storing the JEDEC file in the server’s memory would also enable devices without the necessary file to start the programming sequence, and if the JEDEC file were stored in non-volatile memory, there would be no need to reupload the file even after a server shutdown.

4.3. File system

The WeMos D1 R1 board contains 4 MB of flash memory, of which up to 3 MB can be used for the file system. There are two file systems to use the flash memory of the ESP8266 microcontroller: SPIFFS and LittleFS. Their respective libra- ries are very similar, containing many methods that perform the same functions. However, according to the ESP8266 documentation, SPIFFS is being deprecated and may be removed in future releases, so using the LittleFS file system is recom- mended. [9] LittleFS supports paths of up to 32 characters including the terminating 0, and assumes files to be in the root directory if their path doesn’t start with a slash. Since the file system will be used to store JEDEC files which are independent of each other, only the root directory will be used, where all the files will be stored. That leaves 30 usable characters for file names, including extensions. LittleFS defines the following methods, among others:

• begin(), which mounts the file system • open(), which opens a file in read, write or append mode • remove(), which removes a file given its path Wi-Fi implementation 41

Given these methods, basic file storage functionality can be implemented on the server, since files can be written, modified and removed.

4.4. File system request handlers

Program device As specified in section 4.2, when the user accesses the path “/program”, a request is sent to that path, triggering the execution of the “program device” command. The JEDEC file with which to program the FPGA should be stored in the file system, so a way to refer to it is needed. Since every file has a unique name, that name will be passed as a query parameter in the URL. An HTTP GET request is appropriate to send small amounts of data as query parameters, so this method will be used. The result of the execution is then sent as the payload of a response. If the device is programmed successfully, a response with status code 200 is sent. If not, a res- ponse with status code 500 is sent. A response with status code 404 is sent if the file with which the client requests the FPGA to be programmed does not exist in the file system.

Upload file When the user accesses the path “/upload”, an HTTP POST request is sent to that path, with the JEDEC file as its payload. The file is then written to the file system of the server and a response is sent with status code 201.

Remove file When the user accesses the path “/remove”, an HTTP GET request is sent to that path, with the name of the file to be removed as a query parameter. The result of that execution is then sent as the payload of a response. If the file is removed successfully, a response with status code 200 is sent. If not, a response with status code 500 is sent. A response with status code 404 is sent if the file that the client requests to remove does not exist in the file system. 42 Over-the-air FPGA programming using Arduino

4.5. Tool usability

Having implemented functionality to upload and remove files; program, erase and check the ID of the device, the tool is fully functional. However, it is not practi- cal. Sending HTTP GET requests is easy: entering the URL of the target in the address bar automatically sends a GET request to it; but sending HTTP POST requests is not so easy, especially if there needs to be a file in its payload. To facilitate the use of this tool, a user interface will be made, detailed in the following chapter. For troubleshooting purposes, a request handler for the path “/info” was also added, which responds with information about the file system, since there were some discrepancies between the documentation and the real memory size of the file system. 43

5. User interface

The FPGA programming tool is web-based, so its user interface will be a web page. This will be the main point of interaction between the user and the tool, and it would be convenient to access it without having to specify a path. Since web browsers default to the root path when no path is specified, a request handler for the path “/” will be added to serve the web page, which can be stored in the file system of the server. To make the web page, Hypertext Markup Language (HTML), Cascading Style Sheets (CSS) and JavaScript will be used, since they are the most common lan- guages to define web page content, presentation and behavior, respectively.

Hypertext Markup Language HTML is a markup language used to describe the structure of a web page. An HTML document consists of elements, which the web browser uses to generate the Document Object Model (DOM). The DOM treats the HTML document as a tree structure containing nodes that represent parts of the document. The root node is “document”, which has the “html” child node, which in turn has the “head” and “body” child nodes. The “head” node has child nodes con- taining document metadata, and the “body” node has child nodes representing web page structure. [12]

Figure 21. Document Object Model 44 Over-the-air FPGA programming using Arduino

Cascading Style Sheets CSS is a style sheet language used to describe the presentation of a document written in a markup language. A CSS document consists of selectors followed by their corresponding declaration block. The selector indicates what elements the declaration block applies to, and the declaration block defines the declarations, each containing one property and one value. [13]

JavaScript JavaScript is a programming language that enables interactive web pages. It allows HTML elements and CSS properties to be modified. This will be used along with AJAX (Asynchronous JavaScript and XML) to display status messages. [14 - 15]

5.1. User commands

The “erase” and “check device ID” commands don’t need any data in the payload of the request. Therefore, the commands can be sent with just one user input. Clicking a button is an intuitive way to send a command, since buttons are used very often. They can also include text indicating their action, making it clear what clicking them will do. The “program” and “remove” commands need the name of the file, so there needs to be a way to select a file from the ones in the file system. This can be implemen- ted by adding a file list to the user interface, which allows the user to click on a file to select it. For this reason, a request handler for the path “/list” was added, which responds with a list of JEDEC files stored in the file system. Since the “remove” command only needs to be executed once per file, a remove button can be added next to every file in the list, making the row disappear after the file is removed. The “upload” command needs a file, which can be passed as form data from an HTML form element. This element has to contain one button to choose the file and another button to upload the file. User interface 45

5.2. Status messages

Every command starts an execution which can have a successful or unsuccessful outcome, so status messages should be displayed on the user interface to provide feedback. To do this, the command must be sent and the response must be re- ceived without leaving the web page containing the user interface. AJAX allows sending requests asynchronously; without interfering with the behavior of the web page, receiving a response and modifying the content of the web page accordingly, without the need to reload it. [15] 3 types of status messages are used: successful, unsuccessful and informational. Informational status messages are used to display the name of the file the user chose to upload and the device’s ID after it is retrieved. Successful and unsuccess- ful status messages are used to display the result of an execution.

5.3. Managing the file list

As described in section 5.1, a file list has to be added to the user interface. The list has to display files in a way that allows the user to select them and to remove them. Thus, HTML needs to be generated automatically every time the web page is loaded or a file is uploaded to the file system; and it needs to be deleted every time a file is removed from the file system. To do this, a function to add files to the list and a function to remove files from the list were defined. The function to remove files is called once a successful res- ponse to the “remove” command is received; and the function to add files is called once a successful response to the “upload” command is received, as well as whene- ver the web page is loaded. When the web page is loaded, the interface is reset and a GET request is sent to the path “/list”. When the response is received, the web browser parses it to get the list of files in the file system and calls the function to add files for every file in the list. 46 Over-the-air FPGA programming using Arduino

5.4. Checking JEDEC files

The original code by MoCo Makers included file checks in the “program” com- mand, which prevented Arduino from programming the FPGA if they were un- successful. To speed up the execution of the “program” command, these file checks can be omitted from the execution and replaced with the same checks before the file is uploaded. This saves time on every execution of the “program” command and takes some load off the server.

5.5. Web page responsiveness

The FPGA programming tool can be used on any device which is on the same LAN as the server and is capable of browsing the web. This can include desktop computers, laptops, smartphones, tablets and many others, which can have very different screen sizes. To make the tool more practical for users on different devices, a responsive web de- sign approach can be applied to make the graphical user interface. This approach is based on adapting the layout to the viewport by sizing elements in relative units and using media queries to allow different CSS code to be loaded depending on screen resolution. [16] Two different layouts are used for the user interface: one for devices with narrow screens, such as smartphones; and the other one for devices with wide screens, such as laptops or tablets. The first layout places elements in a single column, minimizing their total width, and the second layout places related elements next to each other, taking advantage of the available width to make the user interface more aesthetic. User interface 47

Figure 22a, b. User interface

49

6. Future updates

The FPGA programming tool is now fully functional and easy to use. However, there may be a need for future updates to add new features, adapt the tool to new technologies, or change the look and feel of the user interface. With this in mind, one more feature was implemented, aimed at developers, which enables remote updating of the HTML file containing the web page. This is a very powerful tool which should be used with caution, since the HTML file contains the internal CSS and JavaScript, meaning that the developer can modify all of the client code, potentially replacing it with malfunctioning code. In this case, functioning code would have to be reuploaded via USB or via an HTTP POST request sent using some other way than the user interface. No feature was implemented to upload the server code remotely. This could be one of the future updates, enabling remote development of the server code. Another potential future update is the use of other HTTP methods. This would require the use of a library supporting those methods, and would improve the co- rrectness of the HTTP requests, since the GET method is not meant to be used for some of the actions it is used for in this project. Future updates aimed at end users could include adding features, such as erasing or programming individual sectors of the FPGA; or customizing the interface, such as adding a dark mode.

51

7. Resource use

7.1. Hardware

This project required the use of a computer, a wireless access point, a WeMos D1 R1 and a Fipsy FPGA Breakout Board. A smartphone was also used to test the responsiveness of the web page containing the user interface. The computer was used to run software development tools, web development tools and web browsers, and the wireless access point was used to establish a LAN to test the functionality of the FPGA programming tool with the ESP8266 operating in station mode.

7.2. Software

The operating systems used by the computer and the smartphone were Windows 10 and Android 9.0, respectively. Software development tools were used to write source code for the Arduino file and the HTML, CSS and JavaScript files. In this project, the Arduino IDE was used to write the code for the ESP8266 and a text editor was used to write the HTML, CSS and JavaScript code. An IDE could have also been used to write the HTML, CSS and JavaScript code, but a text editor was chosen for its lower computational resource use. Web development tools were used to test and debug the code. Most modern web browsers have built-in web development tools, such as Google Chrome, Firefox or Microsoft Edge. In this project, those web browsers were used to test the compa- tibility of the FPGA programming tool. Google Chrome and Firefox for Android were used to test the responsiveness of the web page containing the user interface. 52 Over-the-air FPGA programming using Arduino

7.3. Time and human resources

One person worked on the development of the tool described in this document. Due to social circumstances derived from the COVID-19 pandemic, the available time span was not as long as initially planned, prompting some changes in the scheduling to be made. The total amount of time available for the development of the tool and the wri- ting of the thesis was approximately 9 weeks, starting on July 16th and ending on September 14th. The project was divided in 4 large tasks:

• Original code analysis This task consisted of analyzing and understanding the original code by MoCo Makers, as well as adapting it to the WeMos D1 R1 and testing it. The analysis started before July 16th, as the code was accessible online. However, further understanding of the code, adaptation and testing re- quired the WeMos D1 R1, which was not available to the developer until July 16th. In total, this task took close to 36 hours to complete. • Wi-Fi implementation This task consisted of the development described in chapter 4. It took 63 hours to complete. • User interface development This task consisted of the development described in chapter 5. It took 60 hours to complete. • Thesis writing This task consisted of documenting the necessary information prior to the development of the FPGA programming tool and the tasks completed during development. It took 96 hours to complete.

Figure 23. Gantt chart Resource use 53

7.4. Financial resources

Assuming a wage of 8 € per hour, the total cost of human resources would have been 1272 €.

Table 4. Cost of human resources

Description Time worked [h] Hourly cost [€/h] Total cost [€] Original code analysis 36 8 288 Wi-Fi implementation 63 8 504 UI development 60 8 480 Total 159 1272

Assuming an average computer power consumption of 60 W and an electricity price of 0.12 € per kWh, the cost of electricity is 1.14 €. [17]

Table 5. Cost of electricity

Electricity consumed Unit price [€ / kWh] Total cost [€] [kWh] 9.54 0.12 1.1448

Assuming a computer lifespan of 8000 hours and a price of 1200 €, the cost of amortization is 23.85 €.

Table 6. Cost of amortization

Operational time [h] Hourly cost [€/h] Total cost [€] 159 0.15 23.85 54 Over-the-air FPGA programming using Arduino

The cost of hardware is 46 €.

Table 7. Cost of hardware

Description Units Unit price [€] Total cost [€] WeMos D1 R1 1 10 10 Fipsy FPGA 2 12 24 Breakout Board Shipping 12 Total 46

Adding all these costs, the total cost of the project would have been 1343 €. The software used in this project was free, and the cost of amortization of the smartphone has not been considered because it was used very little in comparison to the computer.

7.5. Environmental resources

The degradation of environmental resources as a result of this project consists mostly of pollution produced during electricity generation and hardware manu- facturing.

Assuming emissions of 240 g CO2 per kWh of energy produced, the emissions of

CO2 produced during electricity generation are 2290 g. [18]

Table 8. Emissions

Energy consumed Emissions Total emissions

[kWh] [g CO2/kWh] [g CO2] 9.54 240 2289.6 55

Conclusions

The initial objective of this project was completed successfully. A tool was created to enable remote programming of an FPGA. Furthermore, it was optimized to re- duce the runtime of commands and improve its robustness, and was implemented in a way that is easy to use. Some future updates to the tool were also proposed based on potential needs of end users. The tool is designed for a specific FPGA from a specific manufacturer, meaning that not every FPGA can be programmed with it. Other devices may need diffe- rent JEDEC files or even other file formats to be programmed, and they might need different commands or even other forms of communication than the ones used in this project. Thus far, this tool only works with devices from the Ma- chXO2 family by Lattice Semiconductor, meaning that its contribution to the current state of FPGA development is very small. This presents an opportunity for further development. More and more people are getting into the world of FPGAs, some of whom may have great ideas for projects which require remote development. Implementing support for other FPGAs could allow those people to tackle their ideas, enabling more users to benefit from this tool. On the other hand, the more users a certain tool has, the more people will try to breach its security. This would quickly beco- me a problem for this tool, as it uses HTTP, a protocol with no encryption. Using HTTPS instead would be an upgrade in the security of the tool, and additional security features could include the option to add passwords. In conclusion, the objectives of this project were fulfilled. However, as the FPGA market expands, the demand for tools facilitating their development grows with it, meaning that there is still a lot of work to be done to satisfy potential future needs of FPGA developers.

57

Bibliography

References

[1] FRENZEL Jr, Louis E. Chapter Thirty-Five - Serial Peripheral Interfa- ce (SPI). Handbook of Serial Communications Interfaces: a comprehen- sive compendium of serial digital input/output (i/o) standards. Newnes, 2016, p. 144. ISBN 9780128006290. [2] Corelis. SPI Tutorial – Serial Peripheral Interface Bus Protocol Basics. [https://www.corelis.com/education/tutorials/spi-tutorial/, July 12, 2020]. [3] AllAboutFPGA.com. FPGA Architecture [https://allaboutfpga.com/ fpga-architecture, July 12, 2020]. [4] Lattice Semiconductor. MachXO2 Family Data Sheet. DS1035 Version 02.0, January 2013. [http://www.latticesemi.com/view_document?do- cument_id=38834, July 21, 2020]. [5] Lattice Semiconductor. MachXO2 Programming and Configuration Usa- ge Guide. TN1204, July 2017. [http://www.latticesemi.com/view_do- cument?document_id=39085, July 21, 2020]. [6] Espressif Systems. ESP8266EX Datasheet. Version 6.5, July 17, 2020. [https://www.espressif.com/sites/default/files/documentation/0a-es- p8266ex_datasheet_en.pdf, July 18, 2020]. [7] Arduino. Arduino Reference - Arduino Reference. [https://www.ardui- no.cc/reference/en/, July 27, 2020]. [8] GitHub. GitHub - MocoMakers/Arduino-Fipsy-Programmer: A pro- grammer for the Fipsy FPGA that works with Arduino. [https://github. com/MocoMakers/Arduino-Fipsy-Programmer, July 12, 2020]. [9] Read the Docs. Welcome to ESP8266 Arduino Core’s documentation! — ESP8266 Arduino Core 2.7.2-92-ga460cb79 documentation. [https:// arduino-esp8266.readthedocs.io/en/latest/, July 27, 2020]. 58 Over-the-air FPGA programming using Arduino

[10] GitHub. Arduino/libraries/ESP8266WebServer at master · esp8266/ Arduino · GitHub. [https://github.com/esp8266/Arduino/tree/master/ libraries/ESP8266WebServer, July 30, 2020]. [11] MDN Web Docs. HTTP | MDN. [https://developer.mozilla.org/en-US/ docs/Web/HTTP, July 30, 2020]. [12] MDN Web Docs. HTML: Hypertext Markup Language | MDN. [https:// developer.mozilla.org/en-US/docs/Web/HTML, August 6, 2020]. [13] MDN Web Docs. CSS: Cascading Style Sheets | MDN. [https://develo- per.mozilla.org/en-US/docs/Web/CSS, August 6, 2020]. [14] MDN Web Docs. JavaScript | MDN. [https://developer.mozilla.org/en- US/docs/Web/javascript, August 6, 2020]. [15] MDN Web Docs. Ajax - Developer guides | MDN. [https://developer. mozilla.org/en-US/docs/Web/Guide/AJAX, August 12, 2020]. [16] MDN Web Docs. Using media queries - CSS: Cascading Style Sheets | MDN. [https://developer.mozilla.org/en-US/docs/Web/CSS/Media_ Queries/Using_media_queries, August 15, 2020]. [17] Tarifadeluz.com. Tarifas de luz de hoy. Precio de la electricidad ahora. [http://www.tarifadeluz.com/, September 12, 2020]. [18] European Environment Agency. CO2 emission intensity — European Environment Agency. [https://www.eea.europa.eu/data-and-maps/da- viz/co2-emission-intensity-5, September 12, 2020].

Complementary bibliography

MoCo Makers. Fipsy FPGA Breakout Board – MoCo Makers. [https://www.mo- comakers.com/fipsy-fpga/, July 12, 2020].

User interface W3Schools. HTML Tutorial. [https://www.w3schools.com/html/, August 6, 2020].

W3Schools. CSS Tutorial. [https://www.w3schools.com/css/, August 12, 2020].

W3Schools. JavaScript Tutorial. [https://www.w3schools.com/js/, August 8, 2020]. 59

Appendices

61

Appendix 1

Arduino code 62 Over-the-air FPGA programming using Arduino Appendix 1 63

#include ​ #include ​ #include ​ #include ​"MachXO2.h" #include ​ #pragma ​hdrstop

// Function declarations bool​ Fipsy_ReadDeviceID(byte ​*​DeviceID); bool​ Fipsy_EraseAll(​void​); char​ JEDEC_SeekNextNonWhitespace(File file); char​ JEDEC_SeekNextKeyChar(File file); byte JEDEC_ReadFuseByte(byte ​*​Fusebyte, File file);

// General purpose subroutine declarations void​ MachXO2_SPITrans(​int​ count); void​ SPI_Transaction(​int​ count);

/* General purpose buffer used to transfer data on SPI * This is bigger than most routines need, but reduces repeated declarations and is bigger than the actual SPI transaction can be, meaning there is always enough room */ byte SPIBuf[​100​];

/* Count of bytes in the SPI buffer * This is filled based on the count to send in a transaction * On return from a transaction, this will specify the number of bytes returned * Bytes returned is the entire SPI transaction, not just the data, so the value should not change unless something went wrong. */ byte MachXO2_Count = -​1​;

// Macro to set the most frequently used dummy values for the SPI buffer byte SPIBUF_DEFAULT[​20​] = { ​0x00​, ​0x00​, ​0x00​, ​0x00​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, 0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​, ​0xFF​ }; #define SPIBUFINIT { MachXO2_Count ​=​ ​0​; memcpy(SPIBuf, SPIBUF_DEFAULT, ​20​); }

64 Over-the-air FPGA programming using Arduino

// Define key elements of SPI transactions #define MachXO2_Command SPIBuf[​0​] #define pMachXO2_Operand (&SPIBuf[1]) #define pMachXO2_Data (&SPIBuf[4])

// Define SPI pins #define SS D10​ // SS #define DATAOUT D11​ // MOSI #define DATAIN D12​ // MISO #define SPICLOCK D13​ // SCLK

// Declare request handlers void​ handleIndex(); void​ handleFaviconICO();

void​ handleFileList(); void​ handleUploadFile(); void​ handleRemoveFile(); void​ handleFileInfo();

void​ handleID(); void​ handleErase(); void​ handleProgram();

// Define soft AP SSID and password const​ ​char​* softAPssid = ​"softAPssid"​; const​ ​char​* softAPpassword = ​"softAPpassword"​;

// Define SSID and password to connect to using station mode const​ ​char​* ssid = ​"ssid"​; const​ ​char​* password = ​"password"​;

// ESP8266WebServer instance to listen on the default HTTP port (80) ESP8266WebServer server;

Appendix 1 65

/* The uploaded file used in handleUploadFile needs to be declared in the global scope, otherwise the resulting uploaded file has 0 bytes. * This is because handleUploadFile is called multiple times to upload one file, writing 2kB of the uploaded file to the LittleFS per function call. */ File uploadedFile;

// Declare FSInfo structure FSInfo fs_info;

void​ setup() { // Set pin modes pinMode(SS, OUTPUT); pinMode(DATAOUT, OUTPUT); pinMode(DATAIN, INPUT); pinMode(SPICLOCK, OUTPUT); pinMode(LED_BUILTIN, OUTPUT);​ // used to indicate code execution

Serial.begin(​115200​); SPI.begin(); LittleFS.begin();

// Set up soft access point ​if​ (WiFi.softAP(softAPssid, softAPpassword)) { Serial.println(​"\n\nSet up soft access point"​); Serial.print(​"SSID: "​); Serial.println(softAPssid); Serial.print(​"Password: "​); Serial.println(softAPpassword); Serial.print(​"IP Address: "​); Serial.println(WiFi.softAPIP()); }

66 Over-the-air FPGA programming using Arduino

// Connect to wireless access point Serial.printf(​"\nConnecting to %s...\n"​, ssid); WiFi.begin(ssid, password);

// Wait for connection to establish ​while​ (WiFi.status() != WL_CONNECTED) { digitalWrite(LED_BUILTIN, LOW); delay(​500​); digitalWrite(LED_BUILTIN, HIGH); delay(​500​); } Serial.print(​"Connected to "​); Serial.println(ssid); Serial.print(​"IP Address: "​); Serial.println(WiFi.localIP());

// Define request handlers server.on(​"/"​, handleIndex); server.on(​"/favicon.ico"​, handleFaviconICO);

server.on(​"/list"​, handleFileList); server.on(​"/upload"​, HTTP_POST, [](){ server.send(​200​, ​"text/plain"​, ​"{\"success\":1}"​); }, handleUploadFile); server.on(​"/remove"​, handleRemoveFile); server.on(​"/info"​, handleFileInfo);

server.on(​"/id"​, handleID); server.on(​"/erase"​, handleErase); server.on(​"/program"​, handleProgram);

server.begin(); }

Appendix 1 67

void​ loop() { server.handleClient(); }

void​ handleIndex() { digitalWrite(LED_BUILTIN, LOW);

// Serve index.html file File file = LittleFS.open(​"/index.html"​, ​"r"​); server.streamFile(file, ​"text/html"​); file.close();

digitalWrite(LED_BUILTIN, HIGH); }

void​ handleFaviconICO() { digitalWrite(LED_BUILTIN, LOW);

// Serve favicon.ico file (icon) File file = LittleFS.open(​"/favicon.ico"​, ​"r"​); server.streamFile(file, ​"image/x-icon"​); file.close();

digitalWrite(LED_BUILTIN, HIGH); }

68 Over-the-air FPGA programming using Arduino

/* handleFileList sends a list of JEDEC files in the file system as JSON. If show=all is passed as a query string, all files are shown, including index.html and favicon.ico */ void​ handleFileList() { digitalWrite(LED_BUILTIN, LOW);

String show = server.arg(​"show"​);​ // parse "show" parameter String list = ​"["​;

// Iterate over files inside the root directory Dir dir = LittleFS.openDir(​"/"​); ​while​ (dir.next()) { File file = dir.openFile(​"r"​); ​if​ ((String(file.name()).endsWith(​".jed"​)) || (show == ​"all"​)) { // Separate by comma if there are multiple files ​if​ (list != ​"["​) { list += ​","​; } // Append file to list list += ​"{\"name\":\""​ + String(file.name()) + ​"\",\"size\":"​ + String(file.size()) + ​"}"​; } file.close(); } list += ​"]"​; server.send(​200​, ​"application/json"​, list); digitalWrite(LED_BUILTIN, HIGH); }

Appendix 1 69

void​ handleUploadFile() { // Define HTTPUpload structure HTTPUpload& upload = server.upload();

​if​ (upload.status == UPLOAD_FILE_START) { digitalWrite(LED_BUILTIN, LOW);

// Create a file with name upload.filename uploadedFile = LittleFS.open(upload.filename, ​"w"​); }

​else​ ​if​ (upload.status == UPLOAD_FILE_WRITE) { ​if​ (uploadedFile) { // Write 2 kB of data to the file system uploadedFile.write(upload.buf, upload.currentSize); } }

​else​ ​if​ (upload.status == UPLOAD_FILE_END) { ​if​ (uploadedFile) { uploadedFile.close(); server.send(​201​, ​"text/plain"​, ​"Uploaded "​ + upload.filename + ​" (size: " + upload.totalSize + ​" B)"​); digitalWrite(LED_BUILTIN, HIGH); } } }

70 Over-the-air FPGA programming using Arduino

void​ handleRemoveFile() { digitalWrite(LED_BUILTIN, LOW);

String filename = server.arg(​"filename"​);​ // parse "filename" parameter

​if​ (!LittleFS.exists(filename)){ server.send(​404​, ​"text/plain"​, ​"File does not exist"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// Check if file is JEDEC ​if​ (!filename.endsWith(​".jed"​)) { server.send(​403​, ​"text/plain"​, ​"Not allowed to remove "​ + filename); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

​if​ (LittleFS.remove(filename))​ // if file is removed successfully { server.send(​200​, ​"text/plain"​, ​"Removed "​ + filename); digitalWrite(LED_BUILTIN, HIGH); ​return​; } server.send(​500​, ​"text/plain"​, ​"Could not remove "​ + filename); digitalWrite(LED_BUILTIN, HIGH); }

Appendix 1 71

// handleFileInfo sends a string with information about the file system void​ handleFileInfo() { digitalWrite(LED_BUILTIN, LOW);

String fileInfo = ​""​;

​if​ (LittleFS.info(fs_info)) { fileInfo += ​"Total bytes: "​ + String(fs_info.totalBytes); fileInfo += ​"\nUsed bytes: "​ + String(fs_info.usedBytes); fileInfo += ​"\nBlock size: "​ + String(fs_info.blockSize); fileInfo += ​"\nPage size: "​ + String(fs_info.pageSize); fileInfo += ​"\nMax open files: "​ + String(fs_info.maxOpenFiles); fileInfo += ​"\nMax path length: "​ + String(fs_info.maxPathLength); }

​else { fileInfo = ​"FSInfo failed"​; } server.send(​200​, ​"text/plain"​, fileInfo); digitalWrite(LED_BUILTIN, HIGH); }

72 Over-the-air FPGA programming using Arduino

void​ handleID() { digitalWrite(LED_BUILTIN, LOW);

byte id[​4​];

// Read and print the IDs ​if​ (!Fipsy_ReadDeviceID(id)) { server.send(​500​, ​"text/plain"​, ​"Data pointer provided is NULL"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// Send ID to client at /id ​char​ ID[​12​]; sprintf(ID, ​"%02X %02X %02X %02X"​, id[​0​], id[​1​], id[​2​], id[​3​]); server.send(​200​, ​"text/plain"​, ID);

digitalWrite(LED_BUILTIN, HIGH); }

void​ handleErase() { digitalWrite(LED_BUILTIN, LOW);

​if​ (!Fipsy_EraseAll()) { server.send(​500​, ​"text/plain"​, ​"Unable to erase device"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

server.send(​200​, ​"text/plain"​, ​"Erase successful"​); digitalWrite(LED_BUILTIN, HIGH); }

Appendix 1 73

void​ handleProgram() { digitalWrite(LED_BUILTIN, LOW);

String filename = server.arg(​"filename"​);​ // parse "filename" parameter

// Declare necessary variables File file; ​char​ key; ​int​ addr_digits = ​0​; byte byteStatus; byte featurerow[​10​]; byte feabits[​4​];

​if​ (!LittleFS.exists(filename)) { server.send(​404​, ​"text/plain"​, ​"File does not exist"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// Check if file is JEDEC ​if​ (!filename.endsWith(​".jed"​)) { server.send(​403​, ​"text/plain"​, ​"File format must be JEDEC (.jed)"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

​if​ (!Fipsy_EraseAll()) { server.send(​500​, ​"text/plain"​, ​"Unable to erase device and proceed with programming"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

74 Over-the-air FPGA programming using Arduino

file = LittleFS.open(filename, ​"r"​); ​if​ (!file) { server.send(​500​, ​"text/plain"​, ​"File open failed"​); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// Read the file characters until the starting STX (0x02) is found ​do { key = file.read(); } ​while​ ((key != ​0x02​) && (key != ​0x03​));

// if there is no STX (checked files will not evaluate true) ​if​ (key == ​0x03​) { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// Next look for the fuse table specifically ​while​ (key != ​'L'​) { ​// checked files will not evaluate to true ​if​ ((key = JEDEC_SeekNextKeyChar(file)) == ​0​) { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; } }

Appendix 1 75

/* We are now at the fuse table and pointing to the starting address. * The documentation says it is followed by white space, but does not really guarantee what kind of white space, so we must read off the address characters one at a time. * We do assume from the documentation that the first L key character found will contain the address zero, and fuse data will start from the beginning of flash. */

// Count the digits in the address ​do { key = file.read(); ​if​ (key == ​'0'​) { addr_digits += ​1​; } } ​while​ (key == ​'0'​);

// Clear the address in the device SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_INIT_ADDRESS; MachXO2_SPITrans(​4​);

/* We are now at the fuse data * Proceed to write flash locations per page until the delimiter is reached. */

​do { // Setup the write and increment command SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_PROG_INCR_NV; pMachXO2_Operand[​2​] = ​0x01​;

// Get the first byte and check that we are not at the delimiter byteStatus = JEDEC_ReadFuseByte(pMachXO2_Data, file);

76 Over-the-air FPGA programming using Arduino

​if​ (byteStatus == ​0​)​ // checked files will not evaluate to true { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// If we did not get the delimiter, this should be a valid row ​if​ (byteStatus != ​'*'​) { // Attempt to collect the rest of the page ​for​ (​int​ i = ​1​; i < ​16​; i++) { byteStatus = JEDEC_ReadFuseByte(&pMachXO2_Data[i], file); ​if​ (byteStatus != ​1​)​ // checked files will not evaluate to true { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; } }

// We can now send the command MachXO2_SPITrans(​20​); delay(​1​); } } ​while​ (byteStatus != ​'*'​);​ // Repeat for every page until the delimiter is reached

/* Note that for our chip the JEDEC file contains two blocks of data. The first is the configuration for the present design, and the second is the remainder of the configuration memory, as the address for the second block changes for each design. This second block of data is ignored. */ Appendix 1 77

// Go find the key for our next thing of interest, the feature row fuses // We just read a delimiter, so we are at the start of the next field

// checked files will not evaluate to true ​if​ ((key = JEDEC_SeekNextNonWhitespace(file)) == ​0​) { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; }

// Look for the key character ​while​ (key != ​'E'​) { ​if​ ((key = JEDEC_SeekNextKeyChar(file)) == ​0​)​ // checked files will not evaluate to true { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; } }

// We are now at the feature row bits, pointed at the fuse data // Read the data into the local arrays ​for​ (​int​ i = ​0​; i < ​8​; i++) { // checked files will not evaluate to true ​if​ (JEDEC_ReadFuseByte(&featurerow[i], file) != ​1​) { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; 78 Over-the-air FPGA programming using Arduino

} } ​for​ (​int​ i = ​0​; i < ​2​; i++) { // checked files will not evaluate to true ​if​ (JEDEC_ReadFuseByte(&feabits[i], file) != ​1​) { server.send(​500​, ​"text/plain"​, ​"File check failed: file format is not valid for JEDEC"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); ​return​; } }

// Call our routine to program these values SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_PROG_FEATURE; memcpy(pMachXO2_Data, featurerow, ​8​); MachXO2_SPITrans(​12​); delay(​1​);

SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_PROG_FEABITS; memcpy(pMachXO2_Data, feabits, ​2​);

// Prevent the SPI from being disabled pMachXO2_Data[​1​] &= ​0xBF​;

// Send the command MachXO2_SPITrans(​6​); delay(​1​);

Appendix 1 79

/* Program the DONE bit (internal) * This effectively tells the SDM (self download mode) that it is allowed to run and allows the device to enter user mode when loading is complete (ie done) */ SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_PROGRAM_DONE; MachXO2_SPITrans(​4​); delay(​1​);

// Now that everything is programmed, reload the configuration from flash SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_REFRESH; MachXO2_SPITrans(​3​); delay(​1​);

SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_PROGRAM_DONE; MachXO2_SPITrans(​4​);

server.send(​200​, ​"text/plain"​, ​"Programming successful"​); file.close(); digitalWrite(LED_BUILTIN, HIGH); }

void​ MachXO2_SPITrans(​int​ count) { MachXO2_Count = count; SPI_Transaction(MachXO2_Count); }

80 Over-the-air FPGA programming using Arduino

/* SPI_Transaction completes the data transfer to and/or from the device per the methods required by this system. This uses the global defined SPI port handle, which is assumed to be open if this call is reached from a routine in this code. It is also assumed that the arguments are valid based on the controlled nature of calls to this routine. */

void​ SPI_Transaction(​int​ count) { byte consumedData[​100​]; byte incomingByte; byte outputValues[count];

memcpy(consumedData, SPIBuf, count);​ // Make a copy of the variable, otherwise SPIBuf gets altered

// Initialize transaction with a maximum SCLK speed of 45 MHz digitalWrite(SS, LOW);​ // SS enabled SPI.beginTransaction(SPISettings(​45000000​, MSBFIRST, SPI_MODE0));

​for​ (​int​ i = ​0​; i < count; i++) { byte sentByte = consumedData[i]; incomingByte = SPI.transfer(sentByte);​ // Send byte and capture returned byte outputValues[i] = incomingByte; }

digitalWrite(SS, HIGH);​ // SS disabled SPI.endTransaction();

​for​ (​int​ i = ​0​; i < count; i++) { SPIBuf[i] = outputValues[i]; } }

Appendix 1 81

/* Fipsy_ReadDeviceID retrieves the device identification number from the FPGA connected to the SPI port and returns true if it succeeded, false otherwise */ bool​ Fipsy_ReadDeviceID(byte ​*​DeviceID) { ​if​ (DeviceID == ​NULL​) { ​return​ ​false​; }

// Construct the command SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_READ_DEVICEID; MachXO2_SPITrans(​8​);

// Copy the data memcpy(DeviceID, pMachXO2_Data, ​4​);

// Return success ​return​ ​true​; }

/* Fipsy_EraseAll clears the configuration from all portions of the FPGA and returns true if it was successful, false otherwise. For this library, this is the first step to programming. This is the function that enters the programming mode, so it must be completed before the programming operation. */

bool​ Fipsy_EraseAll(​void​) { byte busy = ​0x80​; ​uint32_t​ timeout = ​0​;

// Send command to enter offline programming mode SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_ENABLE_OFFLINE; pMachXO2_Operand[​0​] = ​0x08​; MachXO2_SPITrans(​4​); 82 Over-the-air FPGA programming using Arduino

delay(​1​);

// Send command to erase everything SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_ERASE; pMachXO2_Operand[​0​] = ​0x0F​; MachXO2_SPITrans(​4​);

// Look at busy status every so often until it is clear or until timeout // The busy bit is in the MSB, but still means we can test nonzero ​do { // Do a wait between polls of the busy bit delay(​100​); timeout += ​100​; ​if​ (timeout > ​1000​) { ​return​ ​false​; }

// Go read the busy bit SPIBUFINIT; MachXO2_Command = MACHXO2_CMD_CHECK_BUSY; MachXO2_SPITrans(​5​); busy = pMachXO2_Data[​0​]; } ​while​ (busy);

// Return success ​return​ ​true​; }

Appendix 1 83

// JEDEC File Parsing Support Subroutines

/* JEDEC_SeekNextNonWhitespace parses the file until it finds a character that is not white space as defined for a JEDEC file. That character is returned if found. If there is an error, a 0 is returned. Note that the delimiter ('*') is also white space in this context. This is like a line terminator in a sense, so if we have not already read it, we don't want to now read it and return it. This could happen if it is the first character in a file, or if a field ended with "**" or more. These technically null fields should be ignored in this search.

So define white space as ' ', CR, LF, NULL, and '*' at least. But in reality we equally ignore any character less than space, which includes most control characters, including the JEDEC file start and end STX/EOT. */

char​ JEDEC_SeekNextNonWhitespace(File file) { ​char​ c;

// Read until we find something other than whitespace or '*' ​do { c = file.read(); ​if​ (c == ​0x03​)​ // if at end of file { ​return​ ​0​; } } ​while​ ((c <= ​' '​) || (c == ​'*'​));

// Return what was found ​return​ c; }

84 Over-the-air FPGA programming using Arduino

/* JEDEC_SeekNextKeyChar reads the specified file stream until the next key character has been read. The key character (ie key word) is the first character of a field (ie after the previous field's delimiter) after any white space. Thus, in order to do this search, this function will also search for the start of the next field. If we know we are at the start of a field already, then we should not use this routine but instead just look for the character. This routine will find the next key character.

The key character found is returned by value. If the end of the file is reached, or some other error occurs, a 0 is returned. */

char​ JEDEC_SeekNextKeyChar(File file) { ​char​ c; ​char​ key;

// Look for end of line, point to start of next field ​do { c = file.read(); ​if​ (c == ​0x03​)​ // if at end of file { ​return​ ​0​; } } ​while​ (c != ​'*'​);

// Return next non-whitespace character key = JEDEC_SeekNextNonWhitespace(file); ​return​ key; }

Appendix 1 85

/* JEDEC_ReadFuseByte reads from the specified file stream until it has collected eight binary characters and converts those characters to a byte to return by reference. The value returned is 1 if this happened correctly, 0 if an error was encountered, and '*' if the field has ended ('*' found). Unless something is wrong with the file, the full byte will be collected or '*' will be returned. Other white space is automatically removed. A return value of 0 can be interpreted as a format error. A return value of '*' means the caller should assume the file pointer now points to the start of the next field.

Note that anything other than 1,0,* characters can be considered white space. If a character is out of place or replaced, and it disrupts the count of 1s and 0s, then an error will eventually be found. If the bad character is there without affecting the result, then it has no impact this way. In a perfect file, the characters so removed are truly white space. */

byte JEDEC_ReadFuseByte(byte ​*​FuseByte, File file) { ​char​ bits[​10​]; ​char​ c; byte cnt = ​0​;

// Default byte value *FuseByte = ​0​;

​do { c = file.read(); ​if​ (c == ​0x03​)​ // if at end of file { ​return​ ​0​; }

// Record valid characters ​if​ (c == ​'0'​) { bits[cnt++] = ​'0'​; }

86 Over-the-air FPGA programming using Arduino

​if​ (c == ​'1'​) { bits[cnt++] = ​'1'​; }

// If delimiter found, return it ​if​ (c == ​'*'​) { ​return​ c; } } ​while​ (cnt < ​8​);

// Convert the characters to binary ​if​ (bits[​0​] == ​'1'​) *FuseByte += ​128​; ​if​ (bits[​1​] == ​'1'​) *FuseByte += ​64​; ​if​ (bits[​2​] == ​'1'​) *FuseByte += ​32​; ​if​ (bits[​3​] == ​'1'​) *FuseByte += ​16​; ​if​ (bits[​4​] == ​'1'​) *FuseByte += ​8​; ​if​ (bits[​5​] == ​'1'​) *FuseByte += ​4​; ​if​ (bits[​6​] == ​'1'​) *FuseByte += ​2​; ​if​ (bits[​7​] == ​'1'​) *FuseByte += ​1​;

// Return normal success ​return​ ​1​; } 87

Appendix 2

MachXO2 file

Appendix 2 89

// SPI Command Definitions as specified in Table 22 of the of the MachXO2 Programming and Configuration Usage Guide

#define MACHXO2_CMD_READ_DEVICEID ​0xE0 #define MACHXO2_CMD_ENABLE_TRANSPARENT ​0x74 #define MACHXO2_CMD_ENABLE_OFFLINE ​0xC6 #define MACHXO2_CMD_CHECK_BUSY ​0xF0 #define MACHXO2_CMD_READ_STATUS ​0x3C #define MACHXO2_CMD_ERASE ​0x0E #define MACHXO2_CMD_ERASE_UFM ​0xCB #define MACHXO2_CMD_INIT_ADDRESS ​0x46 #define MACHXO2_CMD_WRITE_ADDRESS ​0xB4 #define MACHXO2_CMD_PROG_INCR_NV ​0x70 #define MACHXO2_CMD_INIT_ADDR_UFM ​0x47 #define MACHXO2_CMD_PROG_TAG ​0xC9 #define MACHXO2_CMD_PROGRAM_USERCODE ​0xC2 #define MACHXO2_CMD_READ_USERCODE ​0xC0 #define MACHXO2_CMD_PROG_FEATURE ​0xE4 #define MACHXO2_CMD_READ_FEATURE ​0xE7 #define MACHXO2_CMD_PROG_FEABITS ​0xF8 #define MACHXO2_CMD_READ_FEABITS ​0xFB #define MACHXO2_CMD_READ_INCR_NV ​0x73 #define MACHXO2_CMD_READ_UFM ​0xCA #define MACHXO2_CMD_PROGRAM_DONE ​0x5E #define MACHXO2_CMD_PROG_OTP ​0xF9 #define MACHXO2_CMD_READ_OTP ​0xFA #define MACHXO2_CMD_DISABLE ​0x26 #define MACHXO2_CMD_NOP ​0xFF #define MACHXO2_CMD_REFRESH ​0x79 #define MACHXO2_CMD_PROGRAM_SECURITY ​0xCE #define MACHXO2_CMD_PROGRAM_SECPLUS ​0xCF #define MACHXO2_CMD_READ_UIDCODE ​0x19

/* End of header file */

91

Appendix 3

HTML code 92 Over-the-air FPGA programming using Arduino Appendix 3 93

​ ​​Fipsy FPGA programmer​

CSS file goes here

<​body​> <​h1​>Fipsy FPGA programmer <​div​ class="container"> <​h2​>Fipsy FPGA programmer

<​h3​>Choose ​a​ file to upload <​div​ class="file"> <​div​ class="file__choose"> <​button​ class="button" onclick="chooseFile()" id="chooseFileButton">Choose File

<​div​ class="file__info"> <​span​ class="status info" id="fileName"> <​span​ class="status failure" id="fileStatus">

<​div​ class="file__upload"> <​form​ method="post" enctype="multipart/​form-data​" id="uploadForm"> <​input​ name="file" type="file" accept="​.jed"​ hidden="" id="JEDECfile">

<​input​ class="button" disabled="" name="submit" type="submit" value="Upload File" id="uploadButton">

94 Over-the-air FPGA programming using Arduino

<​span​ class="status" id="uploadStatus">

<​h3​>​Select​ ​a​ file to program device <​div​ class="list"> <​table​ id="fileTable"> <​tr​> <​th​ class="​select-cell​"> <​th​>Name <​th​ class="​size-cell​">Size <​th​>

<​tr​> <​td​ class="​select-cell​"> <​td​ id="listStatus">Loading file list..​.

<​div​ class="actions"> <​div​ class="action"> <​button​ class="button" onclick="program(selectedRow​.cells​[1]​.innerHTML​)" disabled="" id="programButton">Program device

<​span​ class="status" id="programStatus">

<​div​ class="action"> <​button​ class="button" onclick="erase()" id="eraseButton">Erase device

<​span​ class="status" id="eraseStatus">

Appendix 3 95

<​div​ class="action"> <​button​ class="button" onclick="checkID()" id="checkIDButton">Check device ID

<​span​ class="status" id="checkIDStatus">

<​script​ type="text/javascript"> JavaScript file goes here

97

Appendix 4

CSS code

Appendix 4 99

@import url(​'https://fonts.googleapis.com/css2?family=Open+Sans&family=Roboto :wght@300&display=swap'​);

body​ { ​cursor​: ​default​; ​height​: ​100%​; ​width​: ​100%​; ​margin​: ​0px​; ​background-color​: ​#ffefdf​; ​font-family​: ​'Roboto'​, ​sans-serif​; ​font-size​: ​15px​; }

h1​ { ​display​: ​none​; }

h2​ { ​font-family​: ​'Open Sans'​, ​sans-serif​; ​font-size​: ​6.5vw​; ​height​: ​8vw​; ​color​: ​#00bf7f​; ​text-transform​: ​uppercase​; ​text-align​: ​center​; ​margin​: ​0px​ ​0px​ min(​9px​, ​1.5vh​) 0px; }

h3​ { ​font-family​: ​'Open Sans'​, ​sans-serif​; ​font-size​: ​16px​; ​height​: ​20px​; ​align-self​: ​flex-start​; ​margin​: min(​9px​, ​1.5vh​) 0px; }

table​ { ​background​: ​#ffffff​; ​border-collapse​: ​collapse​; ​width​: ​100%​; ​text-align​: ​left​; ​word-break​: ​break-all​; } 100 Over-the-air FPGA programming using Arduino

td​ { ​padding​: ​0px​; }

th​ { ​height​: ​28px​; ​font-size​: ​16px​; ​background​: ​#00bf7f​; ​color​: ​#ffffff​; ​padding​: ​0px​; }

input​[​type​=​"radio"​] { ​cursor​: ​pointer​; ​margin​: ​auto​; }

.container​{ ​display​: ​flex​; ​flex-direction​: ​column​; ​align-items​: ​center​; ​justify-content​: ​center​; ​background​: ​#ffefdf​; ​height​: calc(​100vh​ - ​96px​); ​min-height​: ​300px​; ​padding​: ​20px​; }

.button​{ ​background​: ​#00bf7f​; ​border​: ​none​; ​border-radius​: ​10px​; ​font-family​: ​'Open Sans'​, ​sans-serif​; ​color​: ​#ffffff​; ​cursor​: ​pointer​; ​font-size​: ​16px​; ​text-transform​: ​uppercase​; ​height​: min(​42px​, ​7.2vh​); ​min-height​: ​27px​; ​width​: ​180px​; }

Appendix 4 101

.button:disabled​{ ​background​: ​#bfbfbf​; ​cursor​: ​default​; }

.status​{ ​display​: ​block​; ​font-size​: ​12px​; ​margin-top​: ​3px​; }

.info​{ ​font-size​: ​16px​; ​min-height​: ​20px​; }

.success​{ ​color​: ​#00bf7f​; }

.failure​{ ​color​: ​#df0000​; }

.file​{ ​text-align​: ​center​; }

.file__choose​{ ​margin-bottom​: min(​9px​, ​1.5vh​); }

#uploadStatus​{ ​min-height​: ​15px​; }

.list​{ ​display​: ​flex​; ​flex-grow​: ​1​; ​min-height​: ​72px​; ​width​: ​100%​; ​overflow-y​: ​auto​; ​margin-bottom​: ​9px​; 102 Over-the-air FPGA programming using Arduino

}

#listStatus​{ ​height​: ​28px​; }

.list__row​{ ​border-top​: ​1px​ ​solid​ ​#dfdfdf​; ​transition​: opacity ​1s​; }

.list__row:hover​{ ​cursor​: ​pointer​; }

.select-cell​{ ​max-width​: ​60px​; ​width​: min(​8vw​, ​60px​); ​text-align​: ​center​; }

.size-cell​{ ​display​: ​none​; }

.remove-cell​{ ​padding​: ​6px​; ​text-align​: ​right​; ​width​: ​90px​; }

.remove-button​{ ​background​: ​#bfbfbf​; ​border​: ​none​; ​border-radius​: ​5px​; ​font-family​: ​'Roboto'​, ​sans-serif​; ​color​: ​#ffffff​; ​cursor​: ​pointer​; ​font-size​: ​15px​; ​height​: ​32px​; ​width​: ​96px​; }

Appendix 4 103

.remove-button:disabled​{ ​background​: ​#dfdfdf​; ​cursor​: ​default​; }

.action​{ ​height​: min(​70px​, ​12vh​); ​min-height​: ​48px​; ​text-align​: ​center​; }

@media​ only ​screen​ and (​min-width​: ​768px​) { ​body​ { ​display​: ​flex​; ​flex-direction​: ​column​; ​align-items​: ​center​; ​background-color​: ​#ffffff​; }

​h1​ { ​display​: ​block​; ​font-family​: ​'Open Sans'​, ​sans-serif​; ​font-size​: ​48px​; ​color​: ​#00bf7f​; ​text-transform​: ​uppercase​; ​text-align​: ​center​; ​margin​: ​4vh​ ​0px​; }

​h2​ { ​display​: ​none​; }

​h3​ { ​margin​: ​0px​ ​0px​ min(​9px​, ​2vh​) 0px; }

​.container​{ ​border-radius​: ​40px​; ​height​: calc(​75vh​ - ​50px​); ​min-height​: ​270px​; ​width​: calc(​25vw​ + ​400px​); ​min-width​: ​652.8px​; 104 Over-the-air FPGA programming using Arduino

​padding​: ​4vh​ ​40px​; }

​.file​{ ​display​: ​flex​; ​margin​: ​1vh​ ​0px​ ​2vh​ ​0px​; }

​.file__choose​{ ​display​: ​flex​; }

​.file__info​{ ​width​: ​270px​; ​padding​: ​8.4px​ ​11.4px​ ​0px​ ​11.4px​; ​text-align​: ​left​; }

​.file__upload​{ ​margin​: ​0px​; }

​.list​{ ​margin​: ​1vh​ ​0px​ ​2vh​ ​0px​; }

​.size-cell​{ ​display​: ​revert​; }

​.actions​{ ​display​: ​flex​; ​flex-direction​: ​row​; ​justify-content​: ​space-around​; ​width​: ​100%​; }

​.action​{ ​max-width​: ​180px​; ​min-height​: ​74px​; } }

105

Appendix 5

JavaScript code 106 Over-the-air FPGA programming using Arduino Appendix 5 107

/* Initialize selectedRow and lastStatus, which keep track of the last row the user selected and the last status displayed on screen */ var​ selectedRow = ​null​; var​ lastStatus = ​null​;

// On page load document.addEventListener(​"DOMContentLoaded"​, ​function​(){ ​// Reset buttons, upload form, selected row and last displayed status uploadButton.disabled = ​true​; programButton.disabled = ​true​; uploadForm.reset(); selectedRow = ​null​; lastStatus = ​null​;

​// Send AJAX request to retrieve file list ​const​ xhr = ​new​ XMLHttpRequest(); ​const​ url = ​"/list"​;

xhr.onreadystatechange = ​function​(){ ​// If response is successful ​if​ (​this​.readyState === ​4​ && ​this​.status === ​200​){ ​// Parse list to get array of files array = JSON.parse(​this​.responseText); ​if​ (array.length === ​0​){ listStatus.innerHTML = ​"File list is empty"​; } ​else​{ fileTable.deleteRow(-​1​); ​// delete "Loading file list..." ​for​ (​const​ file ​of​ array){ addToList(file.name, file.size); } } } };

xhr.open(​"GET"​, url); xhr.send(); }); 108 Over-the-air FPGA programming using Arduino

// On choosing a file to upload uploadForm.addEventListener(​"change"​, ​function​(){ ​const​ file = JEDECfile.files[​0​]; uploadButton.disabled = ​true​; fileName.innerHTML = file.name; ​// display file name fileStatus.innerHTML = ​""​; ​// reset file information

​// If file is index.html or favicon.ico, allow upload ​if​ (file.name === ​"index.html"​ || file.name === ​"favicon.ico"​){ uploadButton.disabled = ​false​; ​return​; }

​// If file is not JEDEC ​if​ (!file.name.endsWith(​".jed"​)){ fileStatus.innerHTML = ​"File format must be JEDEC (.jed)"​; ​return​; }

​// If file name is too long ​if​ (file.name.length > ​30​){ fileStatus.innerHTML = ​"File name cannot be longer than 30 characters"​; ​return​; }

​// checkFile will allow the file to be uploaded or not checkFile(file, fileStatus, uploadButton) });

Appendix 5 109

// On file upload uploadForm.addEventListener(​"submit"​, ​function​(e){ uploadButton.disabled = ​true​; uploadButton.value = ​"Uploading..."​; ​if​ (lastStatus){ lastStatus.innerHTML = ​""​; ​// reset last status } ​const​ filename = JEDECfile.files[​0​].name; ​const​ filesize = JEDECfile.files[​0​].size;

​// Prevent default behavior e.preventDefault();

​// Send AJAX request to upload file ​const​ xhr = ​new​ XMLHttpRequest(); ​const​ url = ​"/upload"​;

xhr.onreadystatechange = ​function​(){ ​if​ (​this​.readyState === ​4​){ ​// When response is ready ​// If response is successful ​if​ (​this​.status === ​201​){ uploadStatus.setAttribute(​"class"​, ​"status success"​); uploadStatus.innerHTML = ​"Uploaded "​ + filename;

​// Update JEDEC file in list ​if​ (currentFileRow){ currentFileRow.cells[​2​].innerHTML = filesize + ​" B"​; }

​// Append JEDEC file to list ​else​ ​if​ (filename.endsWith(​".jed"​)){ ​if​ (fileTable.children[​0​].children[​1​].cells[​1​].innerHTML === ​"File list is empty"​){ fileTable.deleteRow(-​1​); } addToList(filename, filesize); } } 110 Over-the-air FPGA programming using Arduino

​// If response failed ​else​{ uploadStatus.setAttribute(​"class"​, ​"status failure"​); uploadStatus.innerHTML = ​"Upload failed"​; }

lastStatus = uploadStatus;

​// Enable upload button if file is correct uploadButton.value = ​"Upload File"​; ​if​ (fileStatus.innerHTML === ​""​){ uploadButton.disabled = ​false​; } } };

xhr.open(​"POST"​, url); xhr.send(​new​ FormData(uploadForm));

​// Check if uploaded file is in list (overwriting) ​let​ currentFileRow = ​null​; ​for​ (​const​ row ​of​ fileTable.children[​0​].children){ ​if​ (row.cells[​1​].innerHTML === filename){ currentFileRow = row; ​break​; } } });

function​ chooseFile(){ JEDECfile.click(); }

Appendix 5 111

function​ checkFile(file, fileStatus, uploadButton){ ​const​ reader = ​new​ FileReader(); reader.readAsText(file); reader.onload = ​function​(){ ​const​ file = reader.result; ​var​ i = ​0​;

​// Look for STX ​while​ (file.charCodeAt(i) != ​2​ && !isNaN(file.charCodeAt(i))){ i++; } ​// If STX is not found ​if​ (i >= file.length){ fileStatus.innerHTML = ​"File check failed: could not find starting STX"​; ​return​; } ​const​ stx = i;

​// Look for QF ​while​ ((file.charCodeAt(i-​3​) != ​10​ || file.charCodeAt(i-​2​) != ​81​ || file.charCodeAt(i-​1​) != ​70​) && !isNaN(file.charCodeAt(i))){ i++; } ​if​ (i >= file.length){ fileStatus.innerHTML = ​"File check failed: could not find 'Q' and qualifier 'F'"​; ​return​; }

​// Count address digits ​var​ addressDigits = ​0​; ​while​ (file.charCodeAt(i) != ​42​){ addressDigits++; i++; }

I++;

112 Over-the-air FPGA programming using Arduino

​// Check that we're not at the end of the file ​if​ (isNaN(file.charCodeAt(i))){ fileStatus.innerHTML = ​"File check failed: address delimiter is at end of file"​; ​return​; }

​// Look for 'E' for feature row ​while​ ((file.charCodeAt(i-​2​) != ​10​ || file.charCodeAt(i-​1​) != ​69​) && !isNaN(file.charCodeAt(i))){ i++; } ​if​ (i >= file.length){ fileStatus.innerHTML = ​"File check failed: could not find 'E' for feature row"​; ​return​; }

​var​ featurerow = ​0​; ​// feature row bit position ​while​ (file.charCodeAt(i) == ​48​ || file.charCodeAt(i) == ​49​){ featurerow++; i++; } ​if​ (featurerow != ​64​){ fileStatus.innerHTML = ​"File check failed: feature row is not 8 bytes long"​; ​return​; }

​// Go to next line ​while​ (file.charCodeAt(i) != ​48​ && file.charCodeAt(i) != ​49​){ i++; } ​var​ feabits = ​0​; ​// feabit position ​while​ (file.charCodeAt(i) == ​48​ || file.charCodeAt(i) == ​49​){ ​if​ (feabits == ​9​ && file.charCodeAt(i) == ​49​){ fileStatus.innerHTML = ​"File check failed: SPI port is disabled"​; ​return​; } Appendix 5 113

feabits++; i++; } ​if​ (feabits != ​16​){ fileStatus.innerHTML = ​"File check failed: feabits is not 2 bytes long"​; ​return​; }

​// Go back to STX i = stx;

​// Look for 'L' ​while​ ((file.charCodeAt(i-​2​) != ​10​ || file.charCodeAt(i-​1​) != ​76​) && !isNaN(file.charCodeAt(i))){ i++; } ​if​ (i >= file.length){ fileStatus.innerHTML = ​"File check failed: could not find 'L' for fuse table"​; ​return​; }

​// Check that address digits are 0 ​const​ limit = i + addressDigits; ​for​ (i; i < limit; i++){ ​if​ (file.charCodeAt(i) != ​48​){ fileStatus.innerHTML = ​"File check failed: fuse table address is not 0"​; ​return​; } }

​// File check successful ​if​ (uploadButton.value !== ​"Uploading..."​){ uploadButton.disabled = ​false​; } } }

114 Over-the-air FPGA programming using Arduino

// On remove file function​ remove(filename){ ​// Get button and row elements removeButton = document.getElementById(​"remove"​ + filename); row = removeButton.parentElement.parentElement;

removeButton.disabled = ​true​; removeButton.innerHTML = ​"Removing..."​;

​/* Reset last status (remove does not display a status: if it succeeds, the row disappears; if not, it resets) */ ​if​ (lastStatus){ lastStatus.innerHTML = ​""​; lastStatus = ​null​; }

​// Send AJAX request to remove file ​const​ xhr = ​new​ XMLHttpRequest(); ​const​ url = ​"/remove?filename="​ + filename;

xhr.onreadystatechange = ​function​(){ ​if​ (​this​.readyState === ​4​){ ​// When response is ready ​// If response is successful ​if​ (​this​.status === ​200​){ ​if​ (selectedRow === row){ programButton.disabled = ​true​; selectedRow = ​null​; } removeFromList(row); }

​// If response failed ​else​{ removeButton.disabled = ​false​; removeButton.innerHTML = ​"Remove file"​; } } }; Appendix 5 115

xhr.open(​"GET"​, url); xhr.send(); }

// On program command function​ program(filename){ programButton.disabled = ​true​; programButton.innerHTML = ​"Programming..."​; ​if​ (lastStatus){ lastStatus.innerHTML = ​""​; }

​// Send AJAX request to program device ​const​ xhr = ​new​ XMLHttpRequest(); ​const​ url = ​"/program?filename="​ + filename;

xhr.onreadystatechange = ​function​(){ ​if​ (​this​.readyState === ​4​){ ​// When response is ready programButton.innerHTML = ​"Program device"​;

​// If response is successful ​if​ (​this​.status === ​200​){ programStatus.setAttribute(​"class"​, ​"status success"​); }

​// If response failed ​else​{ programStatus.setAttribute(​"class"​, ​"status failure"​); }

​// Display status programStatus.innerHTML = ​this​.responseText; lastStatus = programStatus;

116 Over-the-air FPGA programming using Arduino

​/* If a file is selected (always the case, except when the program command was run immediately after the command to remove the same file, not giving it enough time to receive a response) */ ​if​ (selectedRow){ programButton.disabled = ​false​; } } };

xhr.open(​"GET"​, url); xhr.send(); }

// On erase command function​ erase(){ eraseButton.disabled = ​true​; eraseButton.innerHTML = ​"Erasing..."​; ​if​ (lastStatus){ lastStatus.innerHTML = ​""​; }

​// Send AJAX request to erase device ​const​ xhr = ​new​ XMLHttpRequest(); ​const​ url = ​"/erase"​;

xhr.onreadystatechange = ​function​(){ ​if​ (​this​.readyState === ​4​){ ​// When response is ready eraseButton.disabled = ​false​; eraseButton.innerHTML = ​"Erase device"​;

​// If response was successful ​if​ (​this​.status === ​200​){ eraseStatus.setAttribute(​"class"​, ​"status success"​); }

​// If response failed ​else​{ eraseStatus.setAttribute(​"class"​, ​"status failure"​); Appendix 5 117

} eraseStatus.innerHTML = ​this​.responseText; lastStatus = eraseStatus; } };

xhr.open(​"GET"​, url); xhr.send(); }

// On check ID command function​ checkID(){ checkIDButton.disabled = ​true​; checkIDButton.innerHTML = ​"Checking..."​; ​if​ (lastStatus){ lastStatus.innerHTML = ​""​; }

​// Send AJAX request to check device ID ​const​ xhr = ​new​ XMLHttpRequest(); ​const​ url = ​"/id"​;

xhr.onreadystatechange = ​function​(){ ​if​ (​this​.readyState === ​4​){ ​// When response is ready checkIDButton.disabled = ​false​; checkIDButton.innerHTML = ​"Check device ID"​;

​// If response is successful ​if​ (​this​.status === ​200​){ checkIDStatus.setAttribute(​"class"​, ​"status info"​); lastStatus = ​null​; }

​// If response failed ​else​{ checkIDStatus.setAttribute(​"class"​, ​"status failure"​); lastStatus = checkIDStatus; }

118 Over-the-air FPGA programming using Arduino

​// Display status checkIDStatus.innerHTML = ​this​.responseText; } };

xhr.open(​"GET"​, url); xhr.send(); }

// On row select (when a row is clicked) function​ selectRow(e){ ​// If the remove button was clicked ​if​ (e.target.className === ​"remove-button"​){ ​return​; }

​// Deselect previously selected row ​if​ (selectedRow){ selectedRow.cells[​0​].children[​0​].checked = ​false​; selectedRow.style.background = ​"initial"​; }

​// Enable program button ​else​{ programButton.disabled = ​false​; }

​if​ (lastStatus){ lastStatus.innerHTML = ​""​; } lastStatus = ​null​;

​// Select row ​this​.cells[​0​].children[​0​].checked = ​true​; ​this​.style.background = ​"#bfefdf"​; selectedRow = ​this​; }

Appendix 5 119

// On mouse enter function​ enterRow(){ ​if​ (selectedRow !== ​this​){ ​this​.style.background = ​"#efefef"​; } }

// On mouse leave function​ leaveRow(){ ​if​ (selectedRow !== ​this​){ ​this​.style.background = ​"initial"​; } }

// Add a file to the list given its name and size function​ addToList(name, size){ ​const​ row = fileTable.insertRow(-​1​); row.setAttribute(​"class"​, ​"list__row"​);

​const​ selectCell = row.insertCell(​0​); selectCell.setAttribute(​"class"​, ​"select-cell"​); ​const​ nameCell = row.insertCell(​1​); ​const​ sizeCell = row.insertCell(​2​); sizeCell.setAttribute(​"class"​, ​"size-cell"​); ​const​ removeCell = row.insertCell(​3​); removeCell.setAttribute(​"class"​, ​"remove-cell"​);

selectCell.innerHTML = ​""​; nameCell.innerHTML = name; sizeCell.innerHTML = size + ​" B"​; ​// Generate a remove button removeCell.innerHTML = ​""​;

row.addEventListener(​"click"​, selectRow); ​// listen for row selection row.addEventListener(​"mouseenter"​, enterRow); ​// listen for mouse enter row.addEventListener(​"mouseleave"​, leaveRow); ​// listen for mouse leave }

120 Over-the-air FPGA programming using Arduino

// Remove a file from the list given its row function​ removeFromList(row){ ​// Row takes 1s to fade out (CSS transition) row.style.opacity = ​0​; setTimeout(​function​(){ row.remove(); ​// If there are no files in the list, display "File list is empty" ​if​ (fileTable.rows.length === ​1​){ ​const​ row = fileTable.insertRow(-​1​); ​const​ select = row.insertCell(​0​); select.setAttribute(​"class"​, ​"select-cell"​); ​const​ status = row.insertCell(​1​); status.setAttribute(​"id"​, ​"listStatus"​); status.innerHTML = ​"File list is empty"​; } }, ​1000​); }