#include "clock.hpp" #include "uart/uart.hpp" #include #include #include #include #include "command.hpp" using uart_interface = uart::Hardware0, uart::Driven::BLOCKING>; uart::Uart serial; struct Message { uint8_t start; uint8_t number; uint16_t size; uint8_t token; uint8_t body[275]; uint8_t checksum; }; static inline bool receiveByte(uint8_t &data, uint16_t timeout = 1000) { for (uint16_t i = 0; i < timeout; ++i) { if (serial.rxByte(data)) return true; } return false; } static inline uint8_t calcChecksum(const Message &msg) { uint8_t checksum = msg.start; for (uint16_t i = 1; i < 5 + msg.size; ++i) { checksum ^= *(reinterpret_cast(&msg) + i); } return checksum; } static inline bool receiveMessage(Message &msg) { if (!receiveByte(msg.start) || msg.start != MESSAGE_START) return false; if (!receiveByte(msg.number)) return false; if (!receiveByte(*(reinterpret_cast(&msg.size) + 1))) return false; if (!receiveByte(*reinterpret_cast(&msg.size)) || msg.size > sizeof(msg.body)) return false; if (!receiveByte(msg.token) || msg.token != TOKEN) return false; for (uint16_t i = 0; i < msg.size; ++i) { if (!receiveByte(msg.body[i])) return false; } if (!receiveByte(msg.checksum) || msg.checksum != calcChecksum(msg)) return false; return true; } static inline void transmitMessage(const Message &msg) { serial.txByte(msg.start); serial.txByte(msg.number); serial.txByte(msg.size >> 8); serial.txByte(msg.size & 0xFF); serial.txByte(msg.token); for (uint16_t i = 0; i < msg.size; ++i) serial.txByte(msg.body[i]); serial.txByte(msg.checksum); } static inline bool isSignOn(const Message &msg) { if (msg.size == 1 && msg.body[0] == CMD_SIGN_ON) return true; return false; } static inline bool isGetParameter(const Message &msg) { if (msg.size == 2 && msg.body[0] == CMD_GET_PARAMETER) return true; return false; } static inline bool isSetParameter(const Message &msg) { if (msg.size == 3 && msg.body[0] == CMD_SET_PARAMETER) return true; return false; } static inline bool isEnterProgmodeIsp(const Message &msg) { if (msg.size == 12 && msg.body[0] == CMD_ENTER_PROGMODE_ISP) return true; return false; } static inline bool isReadSignatureIsp(const Message &msg) { if (msg.size == 6 && msg.body[0] == CMD_READ_SIGNATURE_ISP) return true; return false; } static inline bool isReadFuseIsp(const Message &msg) { if (msg.size == 6 && msg.body[0] == CMD_READ_FUSE_ISP) return true; return false; } static inline bool isReadLockIsp(const Message &msg) { if (msg.size == 6 && msg.body[0] == CMD_READ_LOCK_ISP) return true; return false; } static inline bool isLoadAddress(const Message &msg) { if (msg.size == 5 && msg.body[0] == CMD_LOAD_ADDRESS) return true; return false; } static inline bool isReadFlashIsp(const Message &msg) { if (msg.size == 4 && msg.body[0] == CMD_READ_FLASH_ISP) return true; return false; } static inline bool isReadEepromIsp(const Message &msg) { if (msg.size == 4 && msg.body[0] == CMD_READ_EEPROM_ISP) return true; return false; } static inline bool isChipEraseIsp(const Message &msg) { if (msg.size == 7 && msg.body[0] == CMD_CHIP_ERASE_ISP) return true; return false; } static inline bool isProgramEepromIsp(const Message &msg) { if (msg.body[0] == CMD_PROGRAM_EEPROM_ISP) { if (msg.size == (static_cast(msg.body[1]) << 8 | msg.body[2]) + 10) return true; } return false; } static inline bool isLeaveProgmodeIsp(const Message &msg) { if (msg.size == 3 && msg.body[0] == CMD_LEAVE_PROGMODE_ISP) return true; return false; } static inline void formatSignOnAnswer(Message &msg) { msg.size = 3 + 8; msg.body[1] = STATUS_CMD_OK; msg.body[2] = 8; msg.body[3] = 'S'; msg.body[4] = 'T'; msg.body[5] = 'K'; msg.body[6] = '5'; msg.body[7] = '0'; msg.body[8] = '0'; msg.body[9] = '_'; msg.body[10] = '2'; msg.checksum = calcChecksum(msg); } static inline void formatGetParameterAnswer(Message &msg) { msg.size = 3; if (msg.body[1] == PARAM_HW_VER) { msg.body[2] = 1; } else if (msg.body[1] == PARAM_SW_MAJOR) { msg.body[2] = 0x02; } else if (msg.body[1] == PARAM_SW_MINOR) { msg.body[2] = 0x0a; } else if (msg.body[1] == PARAM_SCK_DURATION) { msg.body[2] = 2; } else if (msg.body[1] == PARAM_VADJUST) { msg.body[2] = 25; } else if (msg.body[1] == PARAM_VTARGET) { msg.body[2] = 49; } else if (msg.body[1] == PARAM_OSC_PSCALE) { msg.body[2] = 2; } else if (msg.body[1] == PARAM_OSC_CMATCH) { msg.body[2] = 127; } else { msg.size = 2; } if (msg.size == 2) { msg.body[1] = STATUS_CMD_FAILED; } else { msg.body[1] = STATUS_CMD_OK; } msg.checksum = calcChecksum(msg); } static inline void formatSetParameterAnswer(Message &msg) { msg.size = 2; msg.body[1] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatEnterProgmodeIspAnswer(Message &msg) { msg.size = 2; msg.body[1] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatReadSignatureIspAnswer(Message &msg) { msg.size = 4; msg.body[2] = boot_signature_byte_get(msg.body[4] * 2); msg.body[1] = STATUS_CMD_OK; msg.body[3] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatReadFuseIspAnswer(Message &msg) { constexpr auto READ_LOW_FUSE_BITS = 0x0050; constexpr auto READ_HIGH_FUSE_BITS = 0x0858; constexpr auto READ_EXTENDED_FUSE_BITS = 0x0850; msg.size = 4; if (*reinterpret_cast(msg.body + 2) == READ_EXTENDED_FUSE_BITS) { msg.body[2] = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS); } if (*reinterpret_cast(msg.body + 2) == READ_HIGH_FUSE_BITS) { msg.body[2] = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS); } if (*reinterpret_cast(msg.body + 2) == READ_LOW_FUSE_BITS) { msg.body[2] = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS); } msg.body[1] = STATUS_CMD_OK; msg.body[3] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatReadLockIspAnswer(Message &msg) { msg.size = 4; msg.body[2] = boot_lock_fuse_bits_get(GET_LOCK_BITS); msg.body[1] = STATUS_CMD_OK; msg.body[3] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatLoadAddressAnswer(Message &msg) { msg.size = 2; msg.body[1] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatReadFlashIspAnswer(Message &msg, uint32_t &addr) { const uint16_t numBytes = static_cast(msg.body[1]) << 8 | msg.body[2]; msg.size = 3 + numBytes; msg.body[1] = STATUS_CMD_OK; for (uint16_t i = 0; i < numBytes; ++i) { msg.body[i + 2] = pgm_read_byte(static_cast(addr + i)); } addr += numBytes; msg.body[numBytes + 2] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } namespace { bool isEepromReady() { return (EECR & (1 << EEPE)) ? false : true; } void waitEepromReady() { while (!isEepromReady()) ; } uint8_t readEepromByte(const uint8_t *addr) { EEAR = reinterpret_cast(addr); EECR |= (1 << EERE); return EEDR; } void writeEepromByte(uint8_t *addr, uint8_t value) { EECR = 0; EEAR = reinterpret_cast(addr); EEDR = value; EECR |= (1 << EEMPE); EECR |= (1 << EEPE); } } // namespace static inline void performChipErase() { constexpr auto getEepromEraseFuseBit = []() -> bool { constexpr auto EESAVE = 3; return boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) & (1 << EESAVE); }; constexpr auto getBootloaderSize = []() -> uint16_t { const auto highFuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS); constexpr auto BOOTSZ0 = 1; constexpr auto BOOTSZ1 = 2; if (highFuse & (1 << BOOTSZ1) && highFuse & (1 << BOOTSZ0)) return 256 * 2; else if (highFuse & (1 << BOOTSZ1)) return 512 * 2; else if (highFuse & (1 << BOOTSZ0)) return 1024 * 2; return 2048 * 2; }; constexpr auto eraseFlash = [getBootloaderSize]() { const auto bootloaderSize = getBootloaderSize(); const auto flashSize = (FLASHEND - bootloaderSize + 1); for (uint16_t i = 0; i < flashSize; i += SPM_PAGESIZE) { boot_page_erase(i); boot_spm_busy_wait(); } boot_rww_enable(); }; constexpr auto eraseEeprom = [getEepromEraseFuseBit]() { const auto eraseEeprom = getEepromEraseFuseBit(); if (eraseEeprom) { constexpr auto EEPROM_SIZE = E2END + 1; for (uint16_t i = 0; i < EEPROM_SIZE; ++i) { writeEepromByte(reinterpret_cast(i), 0xFF); waitEepromReady(); } } }; eraseFlash(); eraseEeprom(); } static inline void formatChipEraseIspAnswer(Message &msg) { msg.size = 2; msg.body[1] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatReadEepromIspAnswer(Message &msg, uint32_t &addr) { const uint16_t numBytes = static_cast(msg.body[1]) << 8 | msg.body[2]; msg.size = 3 + numBytes; msg.body[1] = STATUS_CMD_OK; for (uint16_t i = 0; i < numBytes; ++i) { msg.body[i + 2] = readEepromByte(reinterpret_cast(addr + i)); } addr += numBytes; msg.body[numBytes + 2] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatProgramEepromIspAnswer(Message &msg, uint32_t &addr) { const uint16_t numBytes = static_cast(msg.body[1]) << 8 | msg.body[2]; for (uint16_t i = 0; i < numBytes; ++i) { writeEepromByte(reinterpret_cast(addr + i), msg.body[10 + i]); waitEepromReady(); } addr += numBytes; msg.size = 2; msg.body[1] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatLeaveProgmodeIspAnswer(Message &msg) { msg.size = 2; msg.body[1] = STATUS_CMD_OK; msg.checksum = calcChecksum(msg); } static inline void formatErrorAnswer(Message &msg) { msg.start = MESSAGE_START; msg.size = 1; msg.token = TOKEN; msg.body[0] = STATUS_CMD_UNKNOWN; msg.checksum = calcChecksum(msg); } enum class ChipEraseState { NONE = 0b00, REQUEST = 0b01, RESPONSE = 0b10, PERFORM = 0b11, }; constexpr ChipEraseState operator|(const ChipEraseState &self, const ChipEraseState &other) { return static_cast(static_cast(self) | static_cast(other)); } constexpr ChipEraseState &operator|=(ChipEraseState &self, const ChipEraseState &other) { self = self | other; return self; } static inline void handleMessage(Message &msg, uint32_t &addr, ChipEraseState &chipEraseFlag) { if (isSignOn(msg)) formatSignOnAnswer(msg); else if (isGetParameter(msg)) formatGetParameterAnswer(msg); else if (isSetParameter(msg)) formatSetParameterAnswer(msg); else if (isEnterProgmodeIsp(msg)) formatEnterProgmodeIspAnswer(msg); else if (isReadSignatureIsp(msg)) formatReadSignatureIspAnswer(msg); else if (isReadFuseIsp(msg)) formatReadFuseIspAnswer(msg); else if (isReadLockIsp(msg)) formatReadLockIspAnswer(msg); else if (isLoadAddress(msg)) { addr = msg.body[1]; addr = (addr << 8) | msg.body[2]; addr = (addr << 8) | msg.body[3]; addr = (addr << 8) | msg.body[4]; formatLoadAddressAnswer(msg); } else if (isReadFlashIsp(msg)) formatReadFlashIspAnswer(msg, addr); else if (isReadEepromIsp(msg)) formatReadEepromIspAnswer(msg, addr); else if (isChipEraseIsp(msg)) { chipEraseFlag = ChipEraseState::REQUEST; formatChipEraseIspAnswer(msg); } else if (isProgramEepromIsp(msg)) formatProgramEepromIspAnswer(msg, addr); else if (isLeaveProgmodeIsp(msg)) { chipEraseFlag |= ChipEraseState::RESPONSE; formatLeaveProgmodeIspAnswer(msg); } else formatErrorAnswer(msg); transmitMessage(msg); if (chipEraseFlag == ChipEraseState::PERFORM) { performChipErase(); chipEraseFlag = ChipEraseState::NONE; } } int main() { serial.init(); Message msg; uint32_t addr = 0x0000; ChipEraseState chipEraseFlag = ChipEraseState::NONE; while (true) { if (receiveMessage(msg)) { handleMessage(msg, addr, chipEraseFlag); } } return 0; } void startup() __attribute__((naked, section(".vectors"))); void startup() { asm volatile("clr __zero_reg__"); // SP = RAMEND; SREG = 0; asm volatile("jmp main"); }