# 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)

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
//---------------------
```
:::