Skip to content

Commit

Permalink
Merge bootloader project as subfolder
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan-b-jakobsson committed Aug 26, 2024
2 parents 0e3bdda + 4b0b78a commit 2fb96bc
Show file tree
Hide file tree
Showing 16 changed files with 1,972 additions and 0 deletions.
15 changes: 15 additions & 0 deletions bootloader/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
BUILD_DIR=build
SRC_FILES=$(wildcard *.asm *.inc)

# Bootloader
$(BUILD_DIR)/bootloader.hex: $(SRC_FILES)
@mkdir -p $(BUILD_DIR)
avra -o $@ -W NoRegDef main.asm
rm -f main.eep.hex
rm -f main.obj
python merge.py
python convert2bin.py

# Clean
clean:
rm -f $(BUILD_DIR)/*
204 changes: 204 additions & 0 deletions bootloader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Purpose

This is a custom bootloader for the Commander X16 ATTiny861 based System Management Controller (SMC).

The purpose of the bootloader is to make it possible to update the SMC firmware from the Commander X16 without using an external programmer.

Firmware data is transmitted from the computer to the SMC over I2C.

# How it works

## SMC memory map

| Byte address | Size | Description |
|-------------- |-------------| ---------------------------|
| 0x0000-0x1DFF | 7,680 bytes | Firmware area |
| 0x0000 | 2 bytes | Reset vector |
| 0x0012 | 2 bytes | EE_RDY vector |
| 0x1E00-0x1FFF | 512 bytes | Bootloader area |
| 0x1E00 | 2 bytes | Bootloader main vector |
| 0x1E02 | 2 bytes | Start update vector |
| 0x1FFE | 2 bytes | Bootloader version |


## SMC reset procedure

On SMC reset the reset vector at address 0x0000 is always executed.
This happens both when mains power is connected to the computer
and when the SMC reset pin #10 is grounded.

When the bootloader is installed, the reset vector is
remapped and always jumps to the bootloader main vector (0x1E00).

The bootloader main function checks if the Reset button is being pressed.
If the button is pressed, the computer is powered on and the
bootloader's firmware update process is started.

If the Reset button was not pressed, the bootloader jumps to
the vector stored in EE_RDY (0x0012). The firmware's original
reset vector is always moved to this address when updating
the firmware.


## Firmware update procedure

The update procedure can be started by holding down the Reset
button as described [above](#smc-reset-procedure).

The procedure can also be started by requesting the
firmware to call the start update vector (0x1e02). This is
done through the I2C command 0x8F (Start bootloader).

While the update procedure is active, firmware data
is transmitted from the CPU to the SMC over I2C as
follows:

- 8 bytes plus 1 checksum byte are transmitted
using the 0x80 I2C command (Transmit).

- The transmitted bytes are committed with
the 0x81 I2C command (Commit).

- Transmitting and committing data is
repeated for the rest of the firmware.

- The flash memory is updated in pages of 64 bytes (pages). On every
8th commit, firmware data will actually be written to flash
memory.

- Just before writing the first flash memory page, all of
the firmware flash area is erased, starting from the last page.

- Finally, the update program is expected to call
the 0x82 I2C command (Reboot) to reset the SMC and power
off the system. This will also write
any remaining data to flash memory.

If the update procedure was started by holding down
the Reset button, the system has no keyboard or mouse
connection. SMCUPDATE-x.x.x.PRG needs to stored as AUTOBOOT.X16
on the SD card so that it is automatically loaded and run.


# Building the project

The firmware hex file (x16-smc.ino.hex) is expected to be found at x16-smc/build. You need
to build or download the firmware and store it there. The firmware is
typically built with the Arduino IDE.

Type ```make``` to build the bootloader.

Build dependencies:

- AVRA assembler https://github.com/Ro5bert/avra

- Python 3

- Python intelhex module, install with pip intelhex


# Fuse settings

The bootloader and the SMC firmware depend on several ATtiny fuse settings as set out below.

The recommended low fuse value is 0xF1. This will run the SMC at 16 MHz.

The recommended high fuse value is 0xD4. This enables Brown-out Detection at 4.3V, which is necessary to prevent flash memory corruption when self-programming is enabled. Serial Programming should be enabled (bit 5) and external reset disable should not be selected (bit 7). These settings are necessary for programming the SMC with an external programmer.

Finally, the extended fuse value must be 0xFE to enable self-programming of the flash memory. The bootloader cannot work without this setting.


# Initial programming of the SMC with avrdude

The initial programming of the SMC must be done with an
external programmer.

The command line utility avrdude is the recommended tool together
with a programmer that is compatible with avrdude.

Example 1. Set fuses
```
avrdude -cstk500v1 -Cavrdude.conf -pattiny861 -P<your port> -b19200 -Ulfuse:w:0xF1:m -Uhfuse:w:0xD4:m -Uefuse:w:0xFE:m
```

Example 2. Write to flash
```
avrdude -cstk500v1 -Cavrdude.conf -pattiny861 -P<your port> -b19200 -Uflash:w:build/firmware_with_bootloader.hex:i
```

The -c option selects programmer-id; stk500v1 is for using Arduino UNO as an In-System Programmer. If you have another ISP programmer, you may need to change this value accordingly.

The -p option selects the target device, always attiny861.

The -P option selects port name on the host computer.

The -b option sets transmission baudrate; 19200 is a good value.

The -U option performs a memory operation. "-U flash:w:filename:i" writes to flash memory. "-U lfuse:w:0xF1:m" writes the low fuse value.

Please note that some fuse settings may cause the ATtiny861 not to respond. Resetting might require equipment for high voltage programming. Be careful if you choose not to use the recommended values.

The Arduino IDE also uses avrdude in the background. If you have installed the IDE can use it to program the SMC, you may enable verbose output and see what parameters are used by the IDE when it starts avrdude.


# I2C API

## Command 0x80 = Transmit (master write)

The transmit command is used to send a data packet to the bootloader.

A packet consists of 8 bytes to be written to flash and 1 checksum byte.

The checksum is the two's complement of the least significant byte of the sum of the previous bytes in the packet. The least significant byte of the sum of all 9 bytes in a packet will consequently always be 0.

## Command 0x81 = Commit (master read)

After a data packet of 9 bytes has been transmitted it must be committed with this command.

The first commit will target flash memory address 0x0000. The target address is moved forward 8 bytes on each successful commit.

The command returns 1 byte. The possible return values are:

Value | Description
------|-------------
1 | OK
2 | Error, packet size not 9
3 | Checksum error
4 | Reserved
5 | Error, overwriting bootloader section

The firmware flash memory are will be erased on the 8th successful commit, just before writing the first 64 bytes to flash memory.

## Command 0x82 = Reboot (master write)

The reboot command must always be called after the last packet
has been committed. If not, the SMC may be left in an inoperable
state.

The command first writes any buffered data to flash.

The bootloader then resets the SMC. The SMC reset shuts down the computer. It
can be restarted by pressing the power button. It is not necessary to power cycle the system
after an update.

## Command 0x83 = Get bootloader version (master read)

This command returns the bootloader version.

Available since bootloader v3.

## Command 0x84 = Rewind target address (master write)

Rewinds the target address to 0.

Available since bootloader v3.

## Command 0x85 = Read flash memory

Reads one byte of flash memory at the current target address.

This function is primarily intended to be used for verifying the
content of the flash memory after an update.

Available since bootloader v3.

5 changes: 5 additions & 0 deletions bootloader/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

avra -o ./build/bootloader.hex main.asm
rm main.eep.hex
rm main.obj
40 changes: 40 additions & 0 deletions bootloader/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Power-on procedure
- Reset vector (0x0000) jumps to start of bootloader memory (0x1e00)
- At start of bootloader memory, there is a jump table. It has two
items (0x1e00=SMC start, and 0x1e02=start update).
- The SMC start checks if the Reset button is held down. If so
it first powers on the system and then starts the update procedure.
- If the Reset button is not held down, the bootloader jumps
to the firmware vector EE_RDY which during the update is made
to hold the firmware's start vector.

## Update procedure
- If the update procedure was started by holding down Reset button
when plugging the X16 to mains power, the computer has no
keyboard. The update program must be autoloaded from the SD card
and run without user interaction.
- If the update was initiated from the X16, it works as before.
- The bootloader I2C is USI driven but without interrupts, as Eirik demonstrated was
possible.
- The same I2C commands as before are used during the update.
- The firmware flash area is erased before writing the first
page of new firmware data. The erase starts from the last page. If the
update is interrupted during this stage, execution will still end up in
the bootloader, as an erased word (0xffff) is a NOP.
- Before writing the first new page, the firmware's reset vector is moved to
the unused EE_RDY, and the reset vector is changed to point to the start of
bootloader memory.
- The subsequent pages are then written to flash memory. If the update
procedure fails during this stage, the bootloader can be started
by holding down the Reset button.
- After all pages have been written the bootloader reset command will
reset and power off the system.

## Backward compatibility
- Current firmware will not know how to get the bootloader version.
The bootloader version is planned to be moved to the end of flash.
Current firmware can, however, start bootloader v3 and do an update anyway.
- The current firmware version may extract the bootloader version using the general flash read function
- Sven's X16-Flash utility will say that there is no bootloader installed, but even so
it continues doing the update. The update will be successful.
- The SMCUPDATE tool aborts the update as it can't determine the bootloader version.
Loading

0 comments on commit 2fb96bc

Please sign in to comment.