From 023332f2b59f754205cd3968623970df7cc90a6a Mon Sep 17 00:00:00 2001 From: akashem06 Date: Sat, 28 Sep 2024 01:33:06 +0000 Subject: [PATCH] Semi-working --- projects/bootloader/inc/boot_can.h | 4 +- projects/bootloader/inc/bootloader.h | 4 +- projects/bootloader/inc/bootloader_mcu.h | 8 +- projects/bootloader/inc/can_datagram.h | 9 +- projects/bootloader/scripts/bootloader_id.py | 3 +- projects/bootloader/scripts/can_datagram.py | 146 ++++++++++++------ .../bootloader/scripts/flash_application.py | 12 +- projects/bootloader/src/bootloader.c | 82 +++++++--- projects/bootloader/src/can_datagram.c | 16 +- projects/bootloader/src/main.c | 2 +- smoke/flash_hal/src/main.c | 11 ++ 11 files changed, 212 insertions(+), 85 deletions(-) diff --git a/projects/bootloader/inc/boot_can.h b/projects/bootloader/inc/boot_can.h index 92c82fd5e..4d001946b 100644 --- a/projects/bootloader/inc/boot_can.h +++ b/projects/bootloader/inc/boot_can.h @@ -22,14 +22,14 @@ typedef enum { typedef enum { CAN_CONTINUOUS = 0, CAN_ONE_SHOT_MODE, NUM_CAN_MODES } Boot_CanMode; -typedef struct Boot_CanSettings { +typedef struct { uint16_t device_id; Boot_CanBitrate bitrate; bool loopback; Boot_CanMode mode; } Boot_CanSettings; -typedef struct Boot_CanMessage { +typedef struct { uint32_t id; uint8_t extended; size_t dlc; diff --git a/projects/bootloader/inc/bootloader.h b/projects/bootloader/inc/bootloader.h index df4be3628..737837861 100644 --- a/projects/bootloader/inc/bootloader.h +++ b/projects/bootloader/inc/bootloader.h @@ -12,7 +12,7 @@ typedef enum { BOOTLOADER_IDLE, /// @brief Bootloader is processing a start message BOOTLOADER_START, - /// @brief Bootloader is waiting for the first byte to be sent (CAN_ARBITRATION_START_FLASH_ID) + /// @brief Bootloader is waiting for the first byte to be sent (CAN_ARBITRATION_SEQUENCING_ID) BOOTLOADER_DATA_READY, /// @brief Bootloader is receiving streamed data and flashing it immediately /// (CAN_ARBITRATION_FLASH_ID) @@ -28,6 +28,8 @@ typedef struct { uintptr_t current_address; uint32_t bytes_written; uint32_t binary_size; + uint32_t packet_crc32; + uint16_t expected_sequence_number; uint16_t buffer_index; BootloaderStates state; diff --git a/projects/bootloader/inc/bootloader_mcu.h b/projects/bootloader/inc/bootloader_mcu.h index 91a443ef2..2a67f7949 100644 --- a/projects/bootloader/inc/bootloader_mcu.h +++ b/projects/bootloader/inc/bootloader_mcu.h @@ -39,5 +39,11 @@ typedef enum { /// @brief Bootloader CAN init error BOOTLOADER_CAN_INIT_ERR, /// @brief Bootloader attempted to write/read to incorrect address - BOOTLOADER_INVALID_ADDRESS + BOOTLOADER_INVALID_ADDRESS, + /// @brief Bootloader has detected a crc mismatch before writing to flash + BOOTLOADER_CRC_MISMATCH_BEFORE_WRITE, + /// @brief Bootloader has detected a crc mismatch after reading the new flash memory + BOOTLOADER_CRC_MISMATCH_AFTER_WRITE, + /// @brief Bootloader received an incorrect sequence number + BOOTLOADER_SEQUENCE_ERROR } BootloaderError; diff --git a/projects/bootloader/inc/can_datagram.h b/projects/bootloader/inc/can_datagram.h index e30e43123..06487dc01 100644 --- a/projects/bootloader/inc/can_datagram.h +++ b/projects/bootloader/inc/can_datagram.h @@ -13,10 +13,10 @@ #define DATA_LEN_SIZE_BYTES 2 #define CAN_ARBITRATION_START_ID 30 -#define CAN_ARBITRATION_START_FLASH_ID 31 +#define CAN_ARBITRATION_SEQUENCING_ID 31 #define CAN_ARBITRATION_FLASH_ID 32 #define CAN_ARBITRATION_JUMP_ID 33 -#define CAN_ARBITRATION_FAULT_ID 34 +#define CAN_ARBITRATION_ACK_ID 34 #define CAN_BITRATE 500000 @@ -29,6 +29,10 @@ typedef struct { uint16_t node_ids; uint32_t data_len; } start; + struct { + uint16_t sequence_num; + uint32_t crc32; + } sequencing; struct { uint8_t *binary_data; } data; @@ -42,3 +46,4 @@ typedef struct { } can_datagram_t; can_datagram_t unpack_datagram(Boot_CanMessage *msg, uint16_t *target_nodes); +void send_ack_datagram(bool ack, BootloaderError error); diff --git a/projects/bootloader/scripts/bootloader_id.py b/projects/bootloader/scripts/bootloader_id.py index 79ac3a043..bb29d7779 100644 --- a/projects/bootloader/scripts/bootloader_id.py +++ b/projects/bootloader/scripts/bootloader_id.py @@ -1,6 +1,7 @@ START = 30 -START_FLASH = 31 +SEQUENCING = 31 FLASH = 32 JUMP = 33 +ACK = 34 STANDARD_CRC32_POLY = 0x04C11DB7 \ No newline at end of file diff --git a/projects/bootloader/scripts/can_datagram.py b/projects/bootloader/scripts/can_datagram.py index 185229c40..bdada774e 100644 --- a/projects/bootloader/scripts/can_datagram.py +++ b/projects/bootloader/scripts/can_datagram.py @@ -6,7 +6,7 @@ from bootloader_id import * DEFAULT_CHANNEL = 'can0' -CAN_BITRATE = 500000 +CAN_BITRATE = 1000000 DATA_SIZE_SIZE = 2 MIN_BYTEARRAY_SIZE = 4 @@ -183,55 +183,113 @@ def send(self, message, sender_id=0): print(can_message) print("Message was sent on {}".format(self.bus.channel_info)) + ack_received = False + retry_count = 0 + max_retries = 3 + + while not ack_received and retry_count < max_retries: + try: + ack_msg = self.bus.recv(timeout=5.0) + + if ack_msg and ack_msg.arbitration_id == ACK: + if ack_msg.data[0] == 0x01: + ack_received = True + print(f"Received ACK for start message") + elif ack_msg.data[0] == 0x00: + print(f"Received NACK for start message, aborting") + break + else: + print(f"Received unknown response for start message, retrying...") + retry_count += 1 + else: + print(f"No ACK/NACK received for start message, retrying...") + retry_count += 1 + + except can.CanError: + print(f"Error waiting for ACK/NACK for start message, retrying...") + retry_count += 1 + + if not ack_received: + raise Exception(f"Failed to receive ACK for start message after {max_retries} attempts") + + + print(f"Start message received succesfully!") + def send_data(self, message, sender_id=0): '''Send a Datagram over CAN''' assert isinstance(message, Datagram) + + message_extended_arbitration = False chunk_messages = list(self._chunkify(message.data, 8)) - crc32_chunks = list(self._chunkify(message.data, 1024)) + sequence_number = 0 - for crc_chunk in crc32_chunks: + while chunk_messages: + seq_num_bytes = sequence_number.to_bytes(2, byteorder='little') + + # Prepare up to 1024 bytes (128 chunks of 8 bytes each) + current_chunk = chunk_messages[:128] + chunk_messages = chunk_messages[128:] + + crc_chunk = b''.join(current_chunk) crc32_value = crc32.calculate(crc_chunk) - print(crc32_value) - crc_data = crc32_value.to_bytes(4, byteorder='big') - print(crc_data)e - - print(crc32_value) - - # Create a CAN message for the CRC32 data - # crc_message = can.Message(arbitration_id=CRC32_CHECK, - # data=crc_data, - # is_extended_id=message_extended_arbitration) - - message_extended_arbitration = False - can_messages = [] - - can_messages.append(can.Message(arbitration_id=START_FLASH, - data=chunk_messages[0], - is_extended_id=message_extended_arbitration)) - - for chunk_message in chunk_messages: - can_messages.append(can.Message(arbitration_id=FLASH, - data=chunk_message, - is_extended_id=message_extended_arbitration)) - binary_sent_counter = 0 - start_time = time.time() - - for msg in can_messages: - try: - # print(msg, "\tBINARY SENT:", binary_sent_counter, "\tTOTAL APP SIZE:", len(message.data)) - self.bus.send(msg) - except BaseException: - # print("Bruh") - time.sleep(0.01) - self.bus.send(msg) - - end_time = time.time() - elapsed_time = end_time - start_time - print( - "{} messages were sent on {}. Took {}".format( - len(can_messages), - self.bus.channel_info, - elapsed_time)) + print(f"CRC32 VALUE: {crc32_value}") + crc_data = crc32_value.to_bytes(4, byteorder='little') + + sequencing_data = seq_num_bytes + crc_data + + # Send sequence message + sequence_msg = can.Message(arbitration_id=SEQUENCING, + data=sequencing_data, + is_extended_id=message_extended_arbitration) + + self.bus.send(sequence_msg) + print(f"Sent sequence message {sequence_number}") + + # Send data chunks (up to 1024 bytes) + for chunk in current_chunk: + time.sleep(0.01) # Small delay between messages + data_msg = can.Message(arbitration_id=FLASH, + data=chunk, + is_extended_id=message_extended_arbitration) + self.bus.send(data_msg) + + print(f"Sent {len(current_chunk) * 8} bytes for sequence {sequence_number}") + + # After sending sequence 0 and its data, start checking for ACK/NACK + if sequence_number > 0 or chunk_messages: + ack_received = False + retry_count = 0 + max_retries = 3 + + while not ack_received and retry_count < max_retries: + try: + ack_msg = self.bus.recv(timeout=5.0) + + if ack_msg and ack_msg.arbitration_id == ACK: + if ack_msg.data[0] == 0x01: + ack_received = True + print(f"Received ACK for sequence {sequence_number}") + elif ack_msg.data[0] == 0x00: + print(f"Received NACK for sequence {sequence_number}, retrying...") + retry_count += 1 + break + else: + print(f"Received unknown response for sequence {sequence_number}, retrying...") + retry_count += 1 + else: + print(f"No ACK/NACK received for sequence {sequence_number}, retrying...") + retry_count += 1 + + except can.CanError: + print(f"Error waiting for ACK/NACK for sequence {sequence_number}, retrying...") + retry_count += 1 + + if not ack_received: + raise Exception(f"Failed to receive ACK for sequence {sequence_number} after {max_retries} attempts") + + sequence_number += 1 + + print(f"All data sent successfully. Total sequences: {sequence_number}") @staticmethod def _chunkify(data, size): diff --git a/projects/bootloader/scripts/flash_application.py b/projects/bootloader/scripts/flash_application.py index c668b0225..e880d3249 100644 --- a/projects/bootloader/scripts/flash_application.py +++ b/projects/bootloader/scripts/flash_application.py @@ -52,6 +52,15 @@ def start_flash(self, **kwargs) -> None: print(f"Starting flash process") print(f'Sending binary data with size of {self._bin_size}...\n\n') + with open(self._bin_path, 'rb') as bin_data: + bin_content = bytearray(bin_data.read()) + + remainder = len(bin_content) % 4 + if remainder != 0: + padding_size = 4 - remainder + bin_content.extend([0x00] * padding_size) + self._bin_size = os.path.getsize(self._bin_path) + padding_size + start_datagram = Datagram( datagram_type_id=bootloader_id.START, node_ids=node_ids, @@ -62,9 +71,6 @@ def start_flash(self, **kwargs) -> None: (self._bin_size >> 24) & 0xff]) ) - with open(self._bin_path, 'rb') as bin_data: - bin_content = bytearray(bin_data.read()) - flash_datagram = Datagram( datagram_type_id=bootloader_id.FLASH, node_ids=node_ids, diff --git a/projects/bootloader/src/bootloader.c b/projects/bootloader/src/bootloader.c index 9637b0648..4a5fd9c00 100644 --- a/projects/bootloader/src/bootloader.c +++ b/projects/bootloader/src/bootloader.c @@ -1,4 +1,5 @@ #include "bootloader.h" +#include "boot_crc32.h" // Store CAN traffic in 1024 byte buffer to write to flash static uint8_t flash_buffer[BOOTLOADER_PAGE_BYTES]; @@ -13,6 +14,8 @@ BootloaderError bootloader_init() { prv_bootloader.binary_size = 0; prv_bootloader.application_start = APP_START_ADDRESS; prv_bootloader.current_address = APP_START_ADDRESS; + prv_bootloader.expected_sequence_number = 0; + prv_bootloader.packet_crc32 = 0; prv_bootloader.state = BOOTLOADER_IDLE; prv_bootloader.target_nodes = 0; prv_bootloader.buffer_index = 0; @@ -55,7 +58,7 @@ static BootloaderError bootloader_switch_states(const BootloaderStates new_state case BOOTLOADER_DATA_RECEIVE: if (new_state == BOOTLOADER_START || new_state == BOOTLOADER_JUMP_APP || - new_state == BOOTLOADER_FAULT) { + new_state == BOOTLOADER_DATA_READY || new_state == BOOTLOADER_FAULT) { prv_bootloader.state = new_state; } else { return_err = BOOTLOADER_INVALID_ARGS; @@ -94,7 +97,7 @@ static BootloaderError bootloader_handle_arbitration_id(Boot_CanMessage *msg) { switch (msg->id) { case CAN_ARBITRATION_START_ID: return bootloader_switch_states(BOOTLOADER_START); - case CAN_ARBITRATION_START_FLASH_ID: + case CAN_ARBITRATION_SEQUENCING_ID: return bootloader_switch_states(BOOTLOADER_DATA_READY); case CAN_ARBITRATION_FLASH_ID: return bootloader_switch_states(BOOTLOADER_DATA_RECEIVE); @@ -110,6 +113,7 @@ static BootloaderError bootloader_start() { for (page = 1 + ((APP_START_ADDRESS - 0x08000000) / BOOTLOADER_PAGE_BYTES); page < NUM_FLASH_PAGES; page++) { if (boot_flash_erase(page)) { + send_ack_datagram(false, BOOTLOADER_FLASH_ERR); return BOOTLOADER_FLASH_ERR; } } @@ -119,11 +123,14 @@ static BootloaderError bootloader_start() { prv_bootloader.buffer_index = 0; prv_bootloader.error = 0; prv_bootloader.first_byte_received = false; + prv_bootloader.expected_sequence_number = 0; if (prv_bootloader.binary_size % BOOTLOADER_WRITE_BYTES != 0) { + send_ack_datagram(false, BOOTLOADER_DATA_NOT_ALIGNED); return BOOTLOADER_DATA_NOT_ALIGNED; } + send_ack_datagram(true, BOOTLOADER_ERROR_NONE); return BOOTLOADER_ERROR_NONE; } @@ -138,45 +145,74 @@ static BootloaderError bootloader_jump_app() { } static BootloaderError bootloader_data_ready() { - memcpy(flash_buffer, datagram.payload.data.binary_data, 8); - prv_bootloader.buffer_index = 8; - prv_bootloader.first_byte_received = true; + if (prv_bootloader.expected_sequence_number == datagram.payload.sequencing.sequence_num) { + prv_bootloader.packet_crc32 = datagram.payload.sequencing.crc32; + prv_bootloader.buffer_index = 0; + if (datagram.payload.sequencing.sequence_num == 0) { + prv_bootloader.first_byte_received = true; + prv_bootloader.bytes_written = 0; + prv_bootloader.current_address = APP_START_ADDRESS; + } + prv_bootloader.expected_sequence_number++; + } else { + // GENERATE NACK, INFORMING THE PYTHON SCRIPT THAT WE MISSED A PACKET + // TODO: Implement NACK generation + send_ack_datagram(false, BOOTLOADER_SEQUENCE_ERROR); + bootloader_switch_states(BOOTLOADER_FAULT); + return BOOTLOADER_SEQUENCE_ERROR; + } return BOOTLOADER_ERROR_NONE; } static BootloaderError bootloader_data_receive() { if (!prv_bootloader.first_byte_received) { + send_ack_datagram(false, BOOTLOADER_INTERNAL_ERR); return BOOTLOADER_INTERNAL_ERR; } - if (prv_bootloader.buffer_index + 8 > BOOTLOADER_PAGE_BYTES) { + if (prv_bootloader.buffer_index >= BOOTLOADER_PAGE_BYTES) { + send_ack_datagram(false, BOOTLOADER_BUFFER_OVERFLOW); return BOOTLOADER_BUFFER_OVERFLOW; } - memcpy(flash_buffer + prv_bootloader.buffer_index, datagram.payload.data.binary_data, 8); - prv_bootloader.buffer_index += 8; + size_t remaining_space = BOOTLOADER_PAGE_BYTES - prv_bootloader.buffer_index; + size_t bytes_to_copy = (remaining_space < 8) ? remaining_space : 8; - if (prv_bootloader.buffer_index == BOOTLOADER_PAGE_BYTES) { - // boot_flash_write(prv_bootloader.current_address, flash_buffer, BOOTLOADER_PAGE_BYTES); + memcpy(flash_buffer + prv_bootloader.buffer_index, datagram.payload.data.binary_data, bytes_to_copy); + prv_bootloader.buffer_index += bytes_to_copy; - prv_bootloader.bytes_written += BOOTLOADER_PAGE_BYTES; - prv_bootloader.current_address += BOOTLOADER_PAGE_BYTES; - prv_bootloader.buffer_index = 0; - } + if (prv_bootloader.buffer_index == BOOTLOADER_PAGE_BYTES || + (prv_bootloader.bytes_written + prv_bootloader.buffer_index) >= prv_bootloader.binary_size) { - if (prv_bootloader.binary_size - prv_bootloader.bytes_written == prv_bootloader.buffer_index) { - // boot_flash_write(prv_bootloader.current_address, flash_buffer, prv_bootloader.buffer_index); - prv_bootloader.bytes_written += prv_bootloader.buffer_index; - prv_bootloader.current_address += prv_bootloader.bytes_written; - prv_bootloader.buffer_index = 0; - } + uint32_t calculated_crc32 = crc_calculate((const uint32_t *)flash_buffer, BYTES_TO_WORD(prv_bootloader.buffer_index)); + if (calculated_crc32 != prv_bootloader.packet_crc32) { + // GENERATE NACK, INFORMING THE PYTHON SCRIPT OF CRC MISMATCH + // TODO: Implement NACK generation + send_ack_datagram(false, BOOTLOADER_CRC_MISMATCH_BEFORE_WRITE); + return BOOTLOADER_CRC_MISMATCH_BEFORE_WRITE; + } + + boot_flash_write(prv_bootloader.current_address, flash_buffer, BOOTLOADER_PAGE_BYTES); - if (prv_bootloader.bytes_written == prv_bootloader.binary_size) { - bootloader_jump_app(); + // boot_flash_read(prv_bootloader.current_address, flash_buffer, prv_bootloader.buffer_index); + // calculated_crc32 = crc_calculate((uint32_t *)flash_buffer, BYTES_TO_WORD(prv_bootloader.buffer_index)); + // if (calculated_crc32 != prv_bootloader.packet_crc32) { + // // GENERATE NACK, INFORMING THE PYTHON SCRIPT OF CRC MISMATCH + // // TODO: Implement NACK generation + // send_ack_datagram(false, BOOTLOADER_CRC_MISMATCH_AFTER_WRITE); + // return BOOTLOADER_CRC_MISMATCH_AFTER_WRITE; + // } + + prv_bootloader.current_address += prv_bootloader.buffer_index; + prv_bootloader.buffer_index = 0; + send_ack_datagram(true, BOOTLOADER_ERROR_NONE); + if (prv_bootloader.bytes_written >= prv_bootloader.binary_size) { + return bootloader_jump_app(); + } } return BOOTLOADER_ERROR_NONE; -}; +} static BootloaderError bootloader_fault() { return BOOTLOADER_INTERNAL_ERR; diff --git a/projects/bootloader/src/can_datagram.c b/projects/bootloader/src/can_datagram.c index 8ac92695d..fb0deb22f 100644 --- a/projects/bootloader/src/can_datagram.c +++ b/projects/bootloader/src/can_datagram.c @@ -14,9 +14,10 @@ can_datagram_t unpack_datagram(Boot_CanMessage *msg, uint16_t *target_nodes) { ((uint32_t)msg->data_u16[2] << 16) | (uint32_t)msg->data_u16[1]; break; - case CAN_ARBITRATION_START_FLASH_ID: + case CAN_ARBITRATION_SEQUENCING_ID: ret_datagram.datagram_type_id = msg->id; - ret_datagram.payload.data.binary_data = msg->data_u8; + ret_datagram.payload.sequencing.sequence_num = msg->data_u16[0]; + ret_datagram.payload.sequencing.crc32 = ((uint32_t)msg->data_u16[1] << 16) | (uint32_t)msg->data_u16[2]; break; case CAN_ARBITRATION_FLASH_ID: ret_datagram.datagram_type_id = msg->id; @@ -34,10 +35,11 @@ can_datagram_t unpack_datagram(Boot_CanMessage *msg, uint16_t *target_nodes) { return ret_datagram; } -BootloaderError can_datagram_transmit_error(BootloaderError error) { - // [ BootloaderError, TBD ] - error_buffer[0] = error; - boot_can_transmit(CAN_ARBITRATION_FAULT_ID, false, error_buffer, sizeof(error_buffer)); +void send_ack_datagram(bool ack, BootloaderError error) { + error_buffer[0] = ack; + uint16_t status_code = error; + error_buffer[1] = (uint8_t) (error << 8); + error_buffer[2] = (uint8_t) (error); - return BOOTLOADER_ERROR_NONE; + boot_can_transmit(CAN_ARBITRATION_ACK_ID, false, error_buffer, sizeof(error_buffer)); } diff --git a/projects/bootloader/src/main.c b/projects/bootloader/src/main.c index 53221510f..32f1e53c9 100644 --- a/projects/bootloader/src/main.c +++ b/projects/bootloader/src/main.c @@ -8,7 +8,7 @@ const Boot_CanSettings can_settings = { .device_id = SYSTEM_CAN_DEVICE_BOOTLOADER, - .bitrate = CAN_HW_BITRATE_500KBPS, + .bitrate = CAN_HW_BITRATE_1000KBPS, .loopback = false, }; diff --git a/smoke/flash_hal/src/main.c b/smoke/flash_hal/src/main.c index b4993eb01..9b897fc01 100644 --- a/smoke/flash_hal/src/main.c +++ b/smoke/flash_hal/src/main.c @@ -2,12 +2,23 @@ #include "log.h" #include "boot_flash.h" +#include "can_board_ids.h" #include "boot_crc32.h" #include "delay.h" #include "tasks.h" #include +const Boot_CanSettings can_settings = { + .device_id = SYSTEM_CAN_DEVICE_BOOTLOADER, + .bitrate = CAN_HW_BITRATE_500KBPS, + .loopback = false, +}; + +Boot_CanMessage msg = { 0 }; + TASK(read_write, TASK_STACK_512) { + boot_can_init(&can_settings); + bootloader_init(); crc_init(); uint8_t flash_buffer[1024]; const uint8_t test_data[] = "Test data for CRC32";