XMODEM Bootloader
Introduction
This describes a bootloader system for the W65C832 and RISC-V FPGA cores by Michael Kohn. I thought it would be nice to be able to load programs into RAM without having to "burn" them into ROM with the processor core itself, which can take several minutes.
To make this possible, Mike designed a simple two-wire UART module. I initially wrote a bootloader which decoded ASCII hexadecimal, which wasn't very reliable. So I switched to the XMODEM protocol, which doesn't require much code to implement, and can work over a basic serial line without handshaking.
Test Setup
- iceFUN iCE40 HX8K FPGA
- Adafruit USB to TTL serial cable
- Minicom 2.9
- naken_asm assembler
Connections are made to the iceFUN FPGA board as follows:
Red |
5V |
not connected |
Black |
GND |
|
White |
RX |
goes to H3 |
Green |
TX |
goes to G3 |
Communications Setup
Terminal software which supports the original XMODEM protocol is required (e.g. Minicom). The serial port should be set to:
- 9600 baud
- 8-bit data
- no parity
- 2 stop bits (1 will work, but 2 may be more reliable)
- no handshaking
Protocol
Character Codes:
SOH |
0x01 |
Start of Header |
EOT |
0x04 |
End of Transmission |
ACK |
0x06 |
Acknowledged |
NAK |
0x15 |
Not Acknowledged |
CAN |
0x18 |
Cancel (ignored by this implementation) |
The iceFUN board has four buttons, defined in icefun.pcf as follows:
- button_halt
- button_0
- button_reset
- button_program_select
Pressing button_reset will display a short message. After initiating an XMODEM transfer in the terminal software, pressing button_0 will begin the download. The other buttons aren't used.
A 132-byte XMODEM packet begins with either an SOH or EOT character. If it's SOH then the download begins (or continues). The remaining 131 bytes are then read all at once:
A few tests are done to provide reasonable assurance that the data is correct:
- Verify that the packet count is the same as the one counted by the client. (Packet count starts at 1).
- Add the packet count and inverse packet count together, which should equal 255.
- Calculate a checksum based on the data retrieved, and compare it to the checksum sent by the host.
(The checksum is simply the low byte of the 16-bit sum of all 128 data bytes in the received packet.)
If a test fails, then a NAK character is sent and the program waits for a new SOH.
If successful, an ACK is sent and the data portion of the packet (128 bytes) is stored into program memory, and the client's packet count is incremented by one. The next SOH signals to continue, or an EOT ends the transmission.
The final packet is padded by the host with extra characters (typically 0x1a) if needed to make it 128 bytes in length.
Details
The bootloader resides in ROM starting at 0x4000. The W65C832/RISC-V cores have 4K ROM and 4K RAM by default. But since the bootloaders require little ROM, the amount defined in rom.v can be smaller, which allows for the amount of RAM defined in ram.v to be proportionally larger. (When changing memory sizes the address bus widths may also need to be adjusted.)
The booloader sends "Ready." to the terminal by default, but this can be customized at the end of the assembly source code file if desired.
On the W65C832, the CPU register widths are set to 8-bits, hence "65C02" instructions are used for the most part. The bootloader fits within 256 bytes of ROM, with room to spare for a longer welcome messag. The downloaded program starts at 0x200, above "zero-page" and the stack.
The RISC-V version loads the program directly into memory starting at location zero. It's register-based and doesn't need a stack. It's a little over 300 bytes.
User programs need to use the correct .org directive: ".org 0x200" for W65C832, ".org 0" for RISC-V.
Programs
The downloaded program is executed immediately after a successful transfer. On the W65C832, memory initially used by the bootloader (0x00-0x1ff) may be used freely.
Pressing the reset button while a program is running restarts the FPGA, allowing a different program (or revision) to be loaded. The assembly source code contains the routines (read_uart and write_uart) which can be copied and used by custom programs which need to interact with the terminal.
Conclusion
Though XMODEM is an older protocol intended to be used over telephone lines, it still worked well for this purpose. The bootloader makes designing software more efficient than "burning" the entire FPGA core over again just to test a different program. And once fully tested, a user program can then replace the bootloader itself.
Source Code
Assembly code is included in the "test" directories of the FPGA core sources. Running "make bootloader" will create the rom.txt file used in the build process.