# PDI-2025: P2 TC header parsing solution ## TC header receive and parsing. Polling and UART interrupt versions Remember that a TC packet has the following structure. In this exercise we are going to parse only the first six bytes (Packet Header) ![](https://i.imgur.com/PYYdKrB.png) Use ```cutecom``` to send a TC like header sequence, for example: ```1B 2C C0 01 00 05``` :::info :information_source: First of all try to understand the code and then execute it. ::: ### Version 0 - Just receive 6 bytes and print their values. Polling version ```c= #include "riscv_types.h" #include "riscv_uart.h" #include "dispatch.h" #include "log.h" #define TC_HEADER_SIZE 6 void process_tc_header( uint8_t tc_header[] ) { printf("\nPROCESS TC PACKET HEADER ... "); for( int i = 0;i < TC_HEADER_SIZE; i++ ) printf("%02X ", tc_header[i]); } int main() { uint8_t car, in_tc = 0; uint32_t idx = 0; int32_t rec = 0; uint8_t tc_header[TC_HEADER_SIZE]; // TX/RX enable riscv_uart_enable_TX(); riscv_uart_enable_RX(); printf("TC HEADER receiver: Polling version\n"); while (1) // Super loop { rec = riscv_getchar(); if ( rec != -1 ) { car = (uint8_t)rec; if ( car == 0x1B ) // Header start ??? { in_tc = 1; // in_tc true } if ( in_tc ) { // Save received value tc_header[ idx ] = car; idx++; } else { printf("Not in TC -> discard data: %02X\n", car ); } // When TC header is complete process it if (idx == TC_HEADER_SIZE) { process_tc_header( tc_header ); in_tc = 0; idx = 0; } } // if } // while(1) return 0; } ``` ### Version 1 - Just receive 6 bytes and print their values. UART Interrupt version ```c= #include "riscv_types.h" #include "riscv_uart.h" #include "dispatch.h" #include "log.h" #define TC_HEADER_SIZE 6 // Global variables: tc_header array and control variables uint32_t idx = 0; uint32_t in_tc = 0; uint32_t flag_tc_header_complete = 0; uint8_t tc_header[TC_HEADER_SIZE]; // UART RX IRQ handler void uart_rx_irq_handler (void) { uint8_t car; car = (uint8_t)riscv_getchar(); if ( car == 0x1B ) // Header start ??? { in_tc = 1; // in_tc true } if ( in_tc ) { // Save received value tc_header[ idx ] = car; idx++; } else { printf("Not in TC -> discard data: %02X\n", rec ); } // When TC header is complete signal setting flag if (idx == TC_HEADER_SIZE) { flag_tc_header_complete = 1; in_tc = 0; idx = 0; } } void process_tc_header( uint8_t tc_header[] ) { printf("\nPROCESS TC PACKET HEADER ... "); for( int i = 0;i < TC_HEADER_SIZE; i++ ) printf("%02X ", tc_header[i]); } int main() { // TX/RX enable riscv_uart_enable_TX(); riscv_uart_enable_RX(); // Install UART RX ISR and enable interrupts processing install_irq_handler( UART_IRQ, uart_rx_irq_handler ); riscv_uart_enable_RI(); // Enable RX irq plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask enable_ext_irq(); // Enable MIE.MEI enable_irq(); // Global interrupt enable printf("TC HEADER receiver: Interrupt version\n"); while (1) // Super loop { if ( flag_tc_header_complete != 0 ) { process_tc_header( tc_header ); flag_tc_header_complete = 0; } // if } // while(1) return 0; } ``` ### Version 2 - Receive 6 bytes packet header and parsing. Polling version ```c= #include "riscv_types.h" #include "riscv_uart.h" #include "dispatch.h" #include "log.h" // Packet id fields #define TC_VERSION_MASK 0xE000 #define TC_VERSION_OFFSET 13 #define TC_TYPE_MASK 0x1000 #define TC_TYPE_OFFSET 12 #define TC_PKT_HDR_MASK 0x0800 #define TC_PKT_HDR_OFFSET 11 #define TC_PKT_APID_MASK 0x07FF // Packet Sequence control fields #define TC_SEQ_FLAGS_MASK 0xC000 #define TC_SEQ_FLAGS_OFFSET 14 #define TC_SEQUENCE_MASK 0x3FFF #define TC_HEADER_SIZE 6 uint16_t deserialize_uint16(uint8_t data_bytes[]) { uint16_t var; var = data_bytes[0]; var = var << 8; var = var | data_bytes[1]; return var; } void process_tc_header( uint8_t tc_header[] ) { printf("\nPROCESS TC PACKET HEADER ... "); for( int i = 0;i < TC_HEADER_SIZE; i++ ) printf("%02X ", tc_header[i]); printf("\n"); // Process TC header uint16_t packet_id = deserialize_uint16( &tc_header[0] ); uint16_t packet_seq_ctrl = deserialize_uint16( &tc_header[2] ); uint16_t packet_length = deserialize_uint16( &tc_header[4] ); // Packet id fields uint8_t version = (packet_id & TC_VERSION_MASK) >> TC_VERSION_OFFSET; uint8_t type = (packet_id & TC_TYPE_MASK) >> TC_TYPE_OFFSET; uint8_t pkt_hdr_flag = (packet_id & TC_PKT_HDR_MASK) >> TC_PKT_HDR_OFFSET; uint16_t apid = (packet_id & TC_PKT_APID_MASK); // Packet Sequence control fields uint8_t seq_flags = (packet_seq_ctrl & TC_SEQ_FLAGS_MASK) >> TC_SEQ_FLAGS_OFFSET; uint16_t packet_sequence = (packet_seq_ctrl & TC_SEQUENCE_MASK); printf("VERSION: %d\n", version ); printf("TYPE: %d\n", type ); printf("PKT SEQ HDR FLAG: %d\n", pkt_hdr_flag ); printf("APID: %d\n", apid ); printf("SEQUENCE FLAGS: %d\n", seq_flags ); printf("PACKET SEQUENCE: %d\n", packet_sequence ); printf("PKT DATA LEN: %d\n", packet_length ); printf("\n"); // Send response riscv_print_string("OK"); } int main() { uint8_t car, in_tc = 0; uint32_t idx = 0; int32_t rec = 0; uint8_t tc_header[TC_HEADER_SIZE]; // TX/RX enable riscv_uart_enable_TX(); riscv_uart_enable_RX(); printf("TC HEADER receiver+processing: Polling version\n"); while (1) // Super loop { rec = riscv_getchar(); if ( rec != -1 ) { car = (uint8_t)rec; if ( car == 0x1B ) // Header start ??? { in_tc = 1; // in_tc true } if ( in_tc ) { // Save received value tc_header[ idx ] = car; idx++; } else { printf("Not in TC -> discard data: %02X\n", car ); } // When TC header is complete signal setting flag if (idx == TC_HEADER_SIZE) { process_tc_header( tc_header ); in_tc = 0; idx = 0; } } // if } // while(1) return 0; } ``` ### Version 3 - Receive 6 bytes packet header and parsing. Interrupt version ```c= #include "riscv_types.h" #include "riscv_uart.h" #include "dispatch.h" #include "log.h" // Packet id fields #define TC_VERSION_MASK 0xE000 #define TC_VERSION_OFFSET 13 #define TC_TYPE_MASK 0x1000 #define TC_TYPE_OFFSET 12 #define TC_PKT_HDR_MASK 0x0800 #define TC_PKT_HDR_OFFSET 11 #define TC_PKT_APID_MASK 0x07FF // Packet Sequence control fields #define TC_SEQ_FLAGS_MASK 0xC000 #define TC_SEQ_FLAGS_OFFSET 14 #define TC_SEQUENCE_MASK 0x3FFF #define TC_HEADER_SIZE 6 // Global variables: tc_header array and control variables uint32_t idx = 0; uint32_t in_tc = 0; uint32_t flag_tc_header_complete = 0; uint8_t tc_header[TC_HEADER_SIZE]; // UART RX IRQ handler void uart_rx_irq_handler (void) { uint8_t car; car = (uint8_t)riscv_getchar(); if ( car == 0x1B ) // Header start ??? { in_tc = 1; // in_tc true } if ( in_tc ) { // Save received value tc_header[ idx ] = car; idx++; } else { printf("Not in TC -> discard data: %02X\n", car ); } // When TC header is complete signal setting flag if (idx == TC_HEADER_SIZE) { flag_tc_header_complete = 1; in_tc = 0; idx = 0; } } uint16_t deserialize_uint16(uint8_t data_bytes[]) { uint16_t var; var = data_bytes[0]; var = var << 8; var = var | data_bytes[1]; return var; } void process_tc_header( uint8_t tc_header[] ) { printf("\nPROCESS TC PACKET HEADER ... "); for( int i = 0;i < TC_HEADER_SIZE; i++ ) printf("%02X ", tc_header[i]); printf("\n"); // Process TC header uint16_t packet_id = deserialize_uint16( &tc_header[0] ); uint16_t packet_seq_ctrl = deserialize_uint16( &tc_header[2] ); uint16_t packet_length = deserialize_uint16( &tc_header[4] ); // Packet id fields uint8_t version = (packet_id & TC_VERSION_MASK) >> TC_VERSION_OFFSET; uint8_t type = (packet_id & TC_TYPE_MASK) >> TC_TYPE_OFFSET; uint8_t pkt_hdr_flag = (packet_id & TC_PKT_HDR_MASK) >> TC_PKT_HDR_OFFSET; uint16_t apid = (packet_id & TC_PKT_APID_MASK); // Packet Sequence control fields uint8_t seq_flags = (packet_seq_ctrl & TC_SEQ_FLAGS_MASK) >> TC_SEQ_FLAGS_OFFSET; uint16_t packet_sequence = (packet_seq_ctrl & TC_SEQUENCE_MASK); printf("VERSION: %d\n", version ); printf("TYPE: %d\n", type ); printf("PKT SEQ HDR FLAG: %d\n", pkt_hdr_flag ); printf("APID: %d\n", apid ); printf("SEQUENCE FLAGS: %d\n", seq_flags ); printf("PACKET SEQUENCE: %d\n", packet_sequence ); printf("PKT DATA LEN: %d\n", packet_length ); printf("\n"); // Send response riscv_print_string("OK"); } int main() { // TX/RX enable riscv_uart_enable_TX(); riscv_uart_enable_RX(); // Install UART RX ISR and enable interrupts processing install_irq_handler( UART_IRQ, uart_rx_irq_handler ); riscv_uart_enable_RI(); // Enable RX irq plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask enable_ext_irq(); // Enable MIE.MEI enable_irq(); // Global interrupt enable printf("TC HEADER receiver+processing: Interrupt version\n"); while (1) // Super loop { if ( flag_tc_header_complete != 0 ) { process_tc_header( tc_header ); flag_tc_header_complete = 0; } // if } // while(1) return 0; } ``` ### Version 4: Time management for incomplete header A timer with a resolution of tenths of a second is required to prevent the programme from locking up in the event that a complete TC is not received. This timer will start counting when the first octet of the TC is received and will have a timeout of 2 seconds. If the complete TC has not been received after this time, the reception will be reset and a new TC will be awaited. You are asked to: Add the requested time management to the code written in the previous point. :::spoiler ```c= // Se añade la variable global contador uint32_t contador = 0; //--------------------- // Hay que añadir la ISR del timer void timer_handler( void ) { if ( contador != 0 ) { contador--; if ( contador == 0 ) { // Reset de las variables de control del TC idx = 0; in_tc = 0; } } } //--------------------- // UART RX IRQ handler void uart_rx_irq_handler (void) { uint8_t car; car = (uint8_t)riscv_getchar(); if ( car == 0x1B ) // Header start ??? { in_tc = 1; // in_tc true // La frecuencia de la ISR es de 10Hz y hay que esperar máximo dos segundos contador = 20; } if ( in_tc ) { // Save received value tc_header[ idx ] = car; idx++; } else { printf("Not in TC -> discard data: %02X\n", car ); } // When TC header is complete signal setting flag if (idx == TC_HEADER_SIZE) { flag_tc_header_complete = 1; in_tc = 0; idx = 0; // Se pone a cero el contador para evitar la cuenta del ISR del timer contador = 0; } } // En la función main hay que añadir la configuración del timer printf("----------TIMER IRQ---------\n"); install_local_timer_handler( timer_handler ); // CLINT timer clock is 10Mhz local_timer_set_gap( 1000000 ); // La frecuencia de la ISR será de 10Hz //--------------------- ``` :::