|
| 1 | + |
| 2 | +# To make the Pi not drop I2C clockspeed to 250kHz from 400kHz, one can say: |
| 3 | +# echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor |
| 4 | + |
| 5 | +# Usage: python firmware_update.py filename.hex |
| 6 | + |
| 7 | +import binascii |
| 8 | + |
| 9 | +import sys |
| 10 | +import time |
| 11 | + |
| 12 | + |
| 13 | +from smbus2 import SMBus, i2c_msg |
| 14 | + |
| 15 | + |
| 16 | +# Small nuvoton: |
| 17 | +bootloader_id = 0xB001 |
| 18 | +bootloader_version = 172 |
| 19 | +bootloader_i2c_addr = 0x6F |
| 20 | +flash_size = 16384-2048 |
| 21 | + |
| 22 | +I2C_WRITE_MAX_SIZE = 32 |
| 23 | + |
| 24 | +REG_APROM_FLASH = 0x10 |
| 25 | +REG_FLASH_PAGE = 0xF0 |
| 26 | +REG_CHIP_ID_L = 0xfa |
| 27 | +REG_CHIP_ID_H = 0xfb |
| 28 | +REG_VERSION = 0xfc |
| 29 | +REG_DEBUG = 0xF8 |
| 30 | +REG_BOOTLOADER_CMD = 0xF3 |
| 31 | + |
| 32 | +REG_CTRL = 0xfe |
| 33 | +MASK_CTRL_FREAD = 0x4 |
| 34 | +MASK_CTRL_FWRITE = 0x8 |
| 35 | + |
| 36 | +PAGE_SIZE = 128 |
| 37 | + |
| 38 | +i2c_dev = SMBus(1) |
| 39 | + |
| 40 | +def i2c_write_bytes(address, start_reg, valuelist): |
| 41 | + assert( len(valuelist) <= I2C_WRITE_MAX_SIZE ) |
| 42 | + msg_w = i2c_msg.write(address, [start_reg] + valuelist) |
| 43 | + i2c_dev.i2c_rdwr(msg_w) |
| 44 | + |
| 45 | + |
| 46 | +def i2c_read_bytes(address, start_reg, length): |
| 47 | + assert( length <= I2C_WRITE_MAX_SIZE ) |
| 48 | + msg_w = i2c_msg.write(address, [start_reg]) |
| 49 | + i2c_dev.i2c_rdwr(msg_w) |
| 50 | + msg_r = i2c_msg.read(address, length) |
| 51 | + i2c_dev.i2c_rdwr(msg_r) |
| 52 | + return list(msg_r) |
| 53 | + |
| 54 | + |
| 55 | +def i2c_read8(address, reg): |
| 56 | + """Read a single (8bit) register from the device.""" |
| 57 | + msg_w = i2c_msg.write(address, [reg]) |
| 58 | + i2c_dev.i2c_rdwr(msg_w) |
| 59 | + msg_r = i2c_msg.read(address, 1) |
| 60 | + i2c_dev.i2c_rdwr(msg_r) |
| 61 | + return list(msg_r)[0] |
| 62 | + |
| 63 | +def i2c_write8(address, reg, value): |
| 64 | + """Write a single (8bit) register to the device.""" |
| 65 | + msg_w = i2c_msg.write(address, [reg, value]) |
| 66 | + i2c_dev.i2c_rdwr(msg_w) |
| 67 | + |
| 68 | +def enter_bootloader(address): |
| 69 | + print("Entering bootloader...") |
| 70 | + i2c_write8(address, REG_CTRL, 0xA0) |
| 71 | + i2c_write8(address, REG_DEBUG, 0xA3) |
| 72 | + time.sleep(0.1) |
| 73 | + |
| 74 | +def set_default_boot(target): |
| 75 | + i2c_write8(bootloader_i2c_addr, REG_BOOTLOADER_CMD, target) |
| 76 | + time.sleep(0.02) |
| 77 | + |
| 78 | +def set_default_boot_bootloader(): |
| 79 | + set_default_boot(2) |
| 80 | + print("Changed default boot to bootloader") |
| 81 | + |
| 82 | +def set_default_boot_main_program(): |
| 83 | + set_default_boot(4) |
| 84 | + print("Changed default boot to main program") |
| 85 | + |
| 86 | + |
| 87 | +def get_chip_id(address): |
| 88 | + """Get the IOE chip ID.""" |
| 89 | + return (i2c_read8(address, REG_CHIP_ID_H) << 8) | i2c_read8(address, REG_CHIP_ID_L) |
| 90 | + |
| 91 | +def confirm_id(address, id): |
| 92 | + chip_id = get_chip_id(address) |
| 93 | + print(f"confirming I2C:{hex(address)}, Chip ID:{hex(id)}, Read ID:{hex(chip_id)}") |
| 94 | + assert( chip_id == id ) |
| 95 | + |
| 96 | +def confirm_bootloader_version(version): |
| 97 | + assert( i2c_read8(bootloader_i2c_addr, REG_VERSION) == version ) |
| 98 | + print("confirmed bootloader version", version) |
| 99 | + |
| 100 | +def set_flash_page(page): |
| 101 | + i2c_write8(bootloader_i2c_addr, REG_FLASH_PAGE, page) |
| 102 | + # print "Page set to", page |
| 103 | + |
| 104 | +def write_page_to_aprom(): |
| 105 | + i2c_write8(bootloader_i2c_addr, REG_CTRL, MASK_CTRL_FWRITE) |
| 106 | + while True: |
| 107 | + time.sleep(0.01) |
| 108 | + try: |
| 109 | + i2c_read8(bootloader_i2c_addr, 0x00) |
| 110 | + break |
| 111 | + except: |
| 112 | + print("waiting for page write to finish...") |
| 113 | + |
| 114 | +def read_page_from_aprom(): |
| 115 | + i2c_write8(bootloader_i2c_addr, REG_CTRL, MASK_CTRL_FREAD) |
| 116 | + time.sleep(0.001) |
| 117 | + |
| 118 | +def jump_to_main_program(): |
| 119 | + i2c_write8(bootloader_i2c_addr, REG_BOOTLOADER_CMD, 1) |
| 120 | + print("Jumped to start main program execution") |
| 121 | + |
| 122 | + |
| 123 | +def firmware_update(bin_data, i2c_address, chip_id): |
| 124 | + assert( len(bin_data) % PAGE_SIZE == 0) |
| 125 | + assert( PAGE_SIZE <= len(bin_data) <= flash_size) |
| 126 | + |
| 127 | + try: |
| 128 | + i2c_read8(bootloader_i2c_addr, 0x00) |
| 129 | + except: |
| 130 | + # We're not in bootloader yet, let's enter first |
| 131 | + confirm_id(i2c_address, chip_id) |
| 132 | + enter_bootloader(i2c_address) |
| 133 | + |
| 134 | + # Ok, we should be in bootloader now |
| 135 | + |
| 136 | + # Ensure bootloader is running with 24MHz clock |
| 137 | + assert( i2c_read8(bootloader_i2c_addr, 0xF7) & 0x10 ) |
| 138 | + |
| 139 | + confirm_id(bootloader_i2c_addr, bootloader_id) |
| 140 | + confirm_bootloader_version(bootloader_version) |
| 141 | + |
| 142 | + set_default_boot_bootloader() |
| 143 | + |
| 144 | + # Flash the program! |
| 145 | + for page in range(len(bin_data)//PAGE_SIZE): |
| 146 | + i=0 |
| 147 | + # for i in range(PAGE_SIZE): |
| 148 | + while i<PAGE_SIZE: |
| 149 | + start_offset = page*PAGE_SIZE + i |
| 150 | + next_offset = page*PAGE_SIZE + i + I2C_WRITE_MAX_SIZE |
| 151 | + # print("Writing bytes from", start_offset, "to", next_offset-1) |
| 152 | + i2c_write_bytes( bootloader_i2c_addr, REG_APROM_FLASH+i, bin_data[ start_offset : next_offset ] ) |
| 153 | + i += I2C_WRITE_MAX_SIZE |
| 154 | + # i2c_write8(bootloader_i2c_addr, REG_APROM_FLASH+i, bin_data[page*PAGE_SIZE + i]) |
| 155 | + set_flash_page(page) |
| 156 | + write_page_to_aprom() |
| 157 | + print(f"Page {page} written to APROM") |
| 158 | + |
| 159 | + print("Code written, verifying...") |
| 160 | + for page in range(len(bin_data)//PAGE_SIZE): |
| 161 | + print("Verifying page", page) |
| 162 | + set_flash_page(page) |
| 163 | + read_page_from_aprom() |
| 164 | + # for i in range(PAGE_SIZE): |
| 165 | + i=0 |
| 166 | + while i< PAGE_SIZE: |
| 167 | + read_length = min(I2C_WRITE_MAX_SIZE, PAGE_SIZE-i) |
| 168 | + read_data = i2c_read_bytes(bootloader_i2c_addr, REG_APROM_FLASH+i, read_length) |
| 169 | + |
| 170 | + start_offset = page*PAGE_SIZE + i |
| 171 | + wr_data = bin_data[ start_offset : start_offset+read_length ] |
| 172 | + |
| 173 | + # print("read_data, wr_data", read_data) |
| 174 | + # print (wr_data) |
| 175 | + |
| 176 | + if (read_data != wr_data): |
| 177 | + print("Flash verification failed, exiting..") |
| 178 | + return |
| 179 | + i += read_length |
| 180 | + |
| 181 | + print("Flash verified, setting the newly flashed program as the default boot option and running it...") |
| 182 | + |
| 183 | + set_default_boot_main_program() |
| 184 | + |
| 185 | + jump_to_main_program() |
| 186 | + |
| 187 | + |
| 188 | + |
| 189 | +if __name__ == "__main__": |
| 190 | + filename = sys.argv[1]; |
| 191 | + # if bin_filename.endswith(".bin"): |
| 192 | + # bin_data = open(bin_filename, "rb").read() |
| 193 | + # bin_data = [ord(x) for x in bin_data] |
| 194 | + # elif bin_filename.endswith(".hex"): |
| 195 | + |
| 196 | + from intelhex import IntelHex |
| 197 | + ih = IntelHex() |
| 198 | + ih.fromfile(filename,format='hex') |
| 199 | + |
| 200 | + target_i2c = ih[200000] |
| 201 | + target_chipid = ih[200001] |
| 202 | + target_chipid += ih[200002] << 8 |
| 203 | + nuvoton_class = ih[200003] |
| 204 | + saved_crc = ih[200004] |
| 205 | + saved_crc += ih[200005] << 8 |
| 206 | + |
| 207 | + assert( nuvoton_class in [16, 32] ) |
| 208 | + |
| 209 | + if nuvoton_class == 32: |
| 210 | + bootloader_id = 0xB004 |
| 211 | + bootloader_version = 211 |
| 212 | + flash_size = 32768-2048 |
| 213 | + |
| 214 | + del ih[200004] |
| 215 | + del ih[200005] |
| 216 | + |
| 217 | + bin_data = list(ih.tobinarray()) |
| 218 | + crc = binascii.crc_hqx(bytearray(bin_data), 0) |
| 219 | + |
| 220 | + # print("crc, saved_crc", hex(crc), hex(saved_crc)) |
| 221 | + assert( saved_crc == crc ) |
| 222 | + |
| 223 | + for addr in range(200000, 200004): |
| 224 | + del ih[addr] |
| 225 | + |
| 226 | + bin_data = list(ih.tobinarray()) |
| 227 | + |
| 228 | + print("Firmware length", len(bin_data)) |
| 229 | + # print(repr(bin_data[:30])) |
| 230 | + |
| 231 | + # chip_id = int( bin_filename.split(".")[0].split("_")[-1] , 16) |
| 232 | + # i2c_address = int( bin_filename.split(".")[0].split("_")[-2] , 16) |
| 233 | + # file_crc = int( bin_filename.split(".")[0].split("_")[-3] , 16) |
| 234 | + # crc = binascii.crc_hqx(str(bin_data), 0) |
| 235 | + # print("Calculated CRC:", hex(crc)) |
| 236 | + # assert( file_crc == crc ) |
| 237 | + |
| 238 | + firmware_update(bin_data, target_i2c, target_chipid) |
0 commit comments