# PDI-2025: P4 Solution
## tm_tc_handling.h
```c=
#ifndef TM_TC_HANDLING_H
#define TM_TC_HANDLING_H
#include "riscv_types.h"
#define IDLE 0
#define READING_CCSDS_HEADER 1
#define READING_PACKET_DF 2
#define TC_HEADER_SIZE 6
void uart_rx_irq_handler(void);
void timer_handler(void);
uint32_t build_response_tm_17_2(uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl);
uint32_t build_response_tm_1_1 (uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl);
uint32_t build_response_tm_1_2 (uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl, uint8_t error);
uint32_t build_response_tm_1_7 (uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl);
uint32_t build_response_tm_1_8 (uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl, uint8_t error);
void sendTM( uint8_t tm_bytes[], uint32_t n );
#endif /* TM_TC_HANDLING_H */
```
## tm_tc_handling.c
```c=
#include "ccsds_pus_format.h"
#include "riscv_uart.h"
#include "serialize.h"
#include "crc.h"
#include "epd_pus_mission.h"
#include "tc_fifo.h"
#include "tm_tc_handling.h"
#include "log.h"
uint32_t idx=0;
uint32_t bytes_to_read=0; // Total bytes to read
uint8_t tc_buffer[TC_FIFO_TAM_TC];
uint32_t receiver_state = IDLE;
uint32_t counter = 0;
//----------
// PHASE 5
void timer_handler(void)
{
if ( counter != 0 )
{
counter--;
if ( counter == 0 )
{
receiver_state = IDLE;
idx=0;
}
}
}
//----------
// PHASE 1
void uart_rx_irq_handler(void) {
uint8_t data;
data = riscv_getchar();
switch (receiver_state)
{
case IDLE:
// TO COMPLETE: Wait for TC HEADER start (0x1B)
//-------
if ( data == 0x1B )
{
tc_buffer[idx] = data;
idx++;
receiver_state = READING_CCSDS_HEADER;
//----------
// PHASE 5
counter = 10; // esperamos 10 ticks de una decima
}
//-------
break;
case READING_CCSDS_HEADER:
// TO COMPLETE: Read TC HEADER
//-------
tc_buffer[idx] = data;
idx++;
if ( idx == TC_HEADER_SIZE )
{
bytes_to_read = deserialize_uint16( &tc_buffer[4] ) + TC_HEADER_SIZE + 1;
receiver_state = READING_PACKET_DF;
}
//-------
break;
case READING_PACKET_DF:
// TO COMPLETE: Read packet data field and checksum
// TO COMPLETE: When TC complete PUSH tc_buffer into FIFO
//-------
tc_buffer[idx] = data;
idx++;
if ( idx == bytes_to_read )
{
tc_fifo_push( &tc_fifo, tc_buffer, idx );
receiver_state = IDLE;
idx=0;
bytes_to_read = 0;
//----------
// PHASE 5
counter = 0; // Se ha recibido el TC completo, desactivamos el contador
}
//-------
break;
}
}
/*
Build TM 17.2 example
*/
uint32_t build_response_tm_17_2(uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl)
{
struct ccds_pus_tmtc_packet_header tm_packet_header;
struct ccds_pus_tm_df_header df_header;
tm_packet_header.packet_id = ccsds_pus_tm_build_packet_id(EPD_APID);
tm_packet_header.packet_seq_ctrl = ccsds_pus_tm_build_packet_seq_ctrl(0x3, tm_seq_count); // verify packet sequence (0x3)
tm_packet_header.packet_length = 4+2 - 1; // pus header + checksum - 1
df_header.version = 0x10; // 0001 0000
df_header.type = 17;
df_header.subtype = 2;
df_header.destinationID = EPD_DESTINATION_ID;
serialize_uint16(tm_packet_header.packet_id, &tm_bytes[0]);
serialize_uint16(tm_packet_header.packet_seq_ctrl, &tm_bytes[2]);
serialize_uint16(tm_packet_header.packet_length, &tm_bytes[4]);
// Store version field into the corresponding byte
tm_bytes[6] = df_header.version;
// Store the remaining fields into their respective locations
tm_bytes[7] = df_header.type;
tm_bytes[8] = df_header.subtype;
tm_bytes[9] = df_header.destinationID;
// This TM has no data
uint16_t crc_value = cal_crc_16(tm_bytes, 6+4); // 6 bytes CCSDS header, 4 bytes PUS header
serialize_uint16(crc_value, &tm_bytes[10]);
return 6+4+2; // packet header + pus header + checksum
}
uint32_t build_response_tm_1_1(uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl)
{
// TO COMPLETE: This TM has app data before checksum
//----------
struct ccds_pus_tmtc_packet_header tm_packet_header;
struct ccds_pus_tm_df_header df_header;
tm_packet_header.packet_id = ccsds_pus_tm_build_packet_id(EPD_APID);
tm_packet_header.packet_seq_ctrl = ccsds_pus_tm_build_packet_seq_ctrl(0x3, tm_seq_count); // verify packet sequence (0x3)
tm_packet_header.packet_length = 4+4+2 - 1; // pus header + APP id (4 bytes) + errorCode (1 byte) + checksum - 1
df_header.version = 0x10; // 0001 0000
df_header.type = 1;
df_header.subtype = 1;
df_header.destinationID = EPD_DESTINATION_ID;
serialize_uint16(tm_packet_header.packet_id, &tm_bytes[0]);
serialize_uint16(tm_packet_header.packet_seq_ctrl, &tm_bytes[2]);
serialize_uint16(tm_packet_header.packet_length, &tm_bytes[4]);
// Store version field into the corresponding byte
tm_bytes[6] = df_header.version;
// Store the remaining fields into their respective locations
tm_bytes[7] = df_header.type;
tm_bytes[8] = df_header.subtype;
tm_bytes[9] = df_header.destinationID;
// This TM has data: TC Packet ID (4 bytes)
serialize_uint32( (uint32_t)tc_packet_id, &tm_bytes[10] );
uint16_t crc_value = cal_crc_16(tm_bytes, 6+4+4); // 6 bytes CCSDS header, 4 bytes PUS header, 4 bytes APP ID
serialize_uint16(crc_value, &tm_bytes[14]);
//----------
return 6+4+4+2; // packet header + pus header + tc_packet_id + checksum
}
uint32_t build_response_tm_1_2(uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl, uint8_t error_code)
{
// TO COMPLETE: This TM has app data and error code before checksum
//----------
struct ccds_pus_tmtc_packet_header tm_packet_header;
struct ccds_pus_tm_df_header df_header;
tm_packet_header.packet_id = ccsds_pus_tm_build_packet_id(EPD_APID);
tm_packet_header.packet_seq_ctrl = ccsds_pus_tm_build_packet_seq_ctrl(0x3, tm_seq_count); // verify packet sequence (0x3)
tm_packet_header.packet_length = 4+4+1+2 - 1; // pus header + APP id (4 bytes) + errorCode (1 byte) + checksum - 1
df_header.version = 0x10; // 0001 0000
df_header.type = 1;
df_header.subtype = 2;
df_header.destinationID = EPD_DESTINATION_ID;
serialize_uint16(tm_packet_header.packet_id, &tm_bytes[0]);
serialize_uint16(tm_packet_header.packet_seq_ctrl, &tm_bytes[2]);
serialize_uint16(tm_packet_header.packet_length, &tm_bytes[4]);
// Store version field into the corresponding byte
tm_bytes[6] = df_header.version;
// Store the remaining fields into their respective locations
tm_bytes[7] = df_header.type;
tm_bytes[8] = df_header.subtype;
tm_bytes[9] = df_header.destinationID;
// This TM has data: TC Packet ID (4 bytes) + ERROR CODE (1 byte)
serialize_uint32( (uint32_t)tc_packet_id, &tm_bytes[10] );
tm_bytes[14] = error_code;
uint16_t crc_value = cal_crc_16(tm_bytes, 6+4+4+1); // 6 bytes CCSDS header, 4 bytes PUS header, 4 bytes APP ID, 1 byte error
serialize_uint16(crc_value, &tm_bytes[15]);
//----------
return 6+4+4+1+2; // packet header + pus header + tc_packet_id + error code + checksum
}
uint32_t build_response_tm_1_7(uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl)
{
// TO COMPLETE: This TM has app data before checksum
//----------
struct ccds_pus_tmtc_packet_header tm_packet_header;
struct ccds_pus_tm_df_header df_header;
tm_packet_header.packet_id = ccsds_pus_tm_build_packet_id(EPD_APID);
tm_packet_header.packet_seq_ctrl = ccsds_pus_tm_build_packet_seq_ctrl(0x3, tm_seq_count); // verify packet sequence (0x3)
tm_packet_header.packet_length = 4+4+2 - 1; // pus header + APP id (4 bytes) + errorCode (1 byte) + checksum - 1
df_header.version = 0x10; // 0001 0000
df_header.type = 1;
df_header.subtype = 7;
df_header.destinationID = EPD_DESTINATION_ID;
serialize_uint16(tm_packet_header.packet_id, &tm_bytes[0]);
serialize_uint16(tm_packet_header.packet_seq_ctrl, &tm_bytes[2]);
serialize_uint16(tm_packet_header.packet_length, &tm_bytes[4]);
// Store version field into the corresponding byte
tm_bytes[6] = df_header.version;
// Store the remaining fields into their respective locations
tm_bytes[7] = df_header.type;
tm_bytes[8] = df_header.subtype;
tm_bytes[9] = df_header.destinationID;
// This TM has data: TC Packet ID (4 bytes)
serialize_uint32( (uint32_t)tc_packet_id, &tm_bytes[10] );
uint16_t crc_value = cal_crc_16(tm_bytes, 6+4+4); // 6 bytes CCSDS header, 4 bytes PUS header, 4 bytes APP ID
serialize_uint16(crc_value, &tm_bytes[14]);
return 6+4+4+2; // packet header + pus header + tc_packet_id + checksum
}
uint32_t build_response_tm_1_8(uint8_t tm_bytes[], uint16_t tm_seq_count, uint16_t tc_packet_id, uint16_t tc_packet_seq_ctrl, uint8_t error)
{
// TO COMPLETE: This TM has app data and error code before checksum
return 6+4+4+1+2; // packet header + pus header + tc_packet_id + error code + checksum
}
void sendTM( uint8_t tm_bytes[], uint32_t tm_len )
{
for (int i=0;i<tm_len;i++)
{
uint8_t error = riscv_putchar( tm_bytes[i]);
if ( error )
break; // discard response
}
}
```
## main.c
```c=
#include "riscv_types.h"
#include "log.h"
#include "riscv_uart.h"
#include "tc_fifo.h"
#include "tm_tc_handling.h"
#include "serialize.h"
#include "dispatch.h"
#include "crc.h"
#include "epd_pus_mission.h"
void processTC(uint8_t tc_bytes[], uint32_t tc_len )
{
uint8_t tm_bytes[TC_FIFO_TAM_TC];
uint32_t tm_len;
uint16_t tm_seq_counter = 1;
uint16_t tc_packet_id = deserialize_uint16( &tc_bytes[0] );
uint16_t tc_packet_seq_ctrl = deserialize_uint16( &tc_bytes[2] );
uint16_t crc_received = deserialize_uint16( &tc_bytes[tc_len-2] );
uint16_t crc_calculated = cal_crc_16(tc_bytes, tc_len - 2);
printf("PacketID: %04X\n", tc_packet_id );
printf("PacketSeqCtrl: %04X\n", tc_packet_seq_ctrl );
printf("[CRCs] Rec: 0x%04X Calc: 0x%04X\n", crc_received, crc_calculated );
if ( crc_received != crc_calculated )
{
//--------
// PHASE 3
printf("ERROR: CRC Error: TM(1,2)\n");
// CRC ERROR: Generate TM (1,2) - Reject
tm_len = build_response_tm_1_2(tm_bytes, tm_seq_counter++, tc_packet_id, tc_packet_seq_ctrl, CRC_ERROR_CODE );
sendTM( tm_bytes, tm_len );
}
else
{
// TC response
uint8_t service_type = tc_bytes[7];
uint8_t service_subtype = tc_bytes[8];
if ( (service_type == 17) && (service_subtype == 1 ) )
{
// TO COMPLETE: Test ACK field
// Bit 3: ack of successful acceptance. 1=TM(1,1) required; 0=TM(1,1) not required
// Bit 2: 0, not used.
// Bit 1: 0, not used.
// Bit 0: 1=TM(1,7) is required; 0=TM(1,7) not required
/*
if TM(1,1) required {
printf("TM(1,1)\n");
build TM(1,1) and sendTM
}
*/
//--------
// PHASE 4 : Processing ACK field (bit 3)
if ( tc_bytes[6] & 0x08 )
{
printf("TM(1,1)\n");
tm_len = build_response_tm_1_1(tm_bytes, tm_seq_counter++, tc_packet_id, tc_packet_seq_ctrl );
sendTM( tm_bytes, tm_len );
}
//--------
//--------
// PHASE 2
printf("TC(17,1) ---> TM(17,2)\n");
// build TM(17,2) and sendTM
tm_len = build_response_tm_17_2(tm_bytes, tm_seq_counter++, tc_packet_id, tc_packet_seq_ctrl );
sendTM( tm_bytes, tm_len );
//--------
/*
if TM1,7) required {
printf("TM(1,7)\n");
build TM(1,7) and sendTM
}
*/
//--------
// PHASE 4 : Processing ACK field (bit 0)
if ( tc_bytes[6] & 0x01 )
{
printf("TM(1,7)\n");
tm_len = build_response_tm_1_7(tm_bytes, tm_seq_counter++, tc_packet_id, tc_packet_seq_ctrl );
sendTM( tm_bytes, tm_len );
}
}
else
{
//--------
// PHASE 3
printf("ERROR: Service not supported: TM(1,2)\n");
// Error: service nor supported TM(1,2) - Reject
tm_len = build_response_tm_1_2(tm_bytes, tm_seq_counter++, tc_packet_id, tc_packet_seq_ctrl, SERVICE_UNKNOWN_ERROR_CODE);
sendTM( tm_bytes, tm_len );
}
} // else CRC
} // processTC
int main()
{
uint8_t tc_bytes[TC_FIFO_TAM_TC];
uint32_t tc_len;
tc_fifo_init( &tc_fifo ); // tc_fifo is declared in tm_tc_handling.c
printf("TM/TC RISCV Handler\n");
// TX/RX enable
riscv_uart_enable_TX();
riscv_uart_enable_RX();
// E/S por interrupción con mapeo socket
install_irq_handler( UART_IRQ, uart_rx_irq_handler ); // Install uart handler
riscv_uart_enable_RI(); // Enable RX irq
plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask
enable_ext_irq(); // Enable MIE.MEI
install_local_timer_handler( timer_handler );
local_timer_set_gap( 1000000 ); // CLINT timer clock is 10Mhz
enable_timer_clinc_irq();
enable_irq(); // Global interrupt enable
while(1)
{
/*
* if FIFO is not empty
* POP TC in mutual exclusion to avoid UART IRQ
* Process TC
*/
//----------
if ( tc_fifo_not_empty( &tc_fifo) )
{
plic_irq_mask( UART_IRQ ); // PLIC UART irq mask
tc_fifo_pop( &tc_fifo, tc_bytes, &tc_len );
plic_irq_unmask( UART_IRQ ); // PLIC UART irq unmask
// print_buffer( tc_bytes, tc_len );
processTC( tc_bytes, tc_len );
}
//----------
}
return 0;
}
```