# PDI-2025: Classroom exercises The following exercises are based on the topics covered in class. ## Bit clear example ![image](https://hackmd.io/_uploads/BkzigU3jxl.png) ## Multibit field clear and set The following figure describe the bits layout of a SPI ctrl register. ![imagen](https://hackmd.io/_uploads/SyIOYp62a.png) The ```LEN``` field is made up of bits 23-20. Assume you have pointer already initialized to the memory where register is mapped ```uint32_t *pREG = <register address>;``` and the following code: ```c= // Set LEN to 0xF *pREG |= (0xF << 20); // Now change LEN to 3 *pREG |= (0x3 << 20); ``` :::warning :question: Does the LEN field really change to 3?, What is wrong with the previous code? ::: :::spoiler ```c= // Set LEN to 0xF *pREG |= (0xF << 20); // LEN field must be set to 0 before setting the new value *pREG &= ~(0xF << 20); // Now change LEN to 3 *pREG |= (0x3 << 20); ``` ::: ## UART polling I/O time considerations Lets have a UART serial line connected to another system with the following configuration: * 1 bit start, 8 bits data, 1 bit stop, no parity * 9600 bauds speed * No flow control * No receive FIFO :::warning :question: What would be the minimum polling period that the software have to carry out in order to avoid overrun errors? ::: :::spoiler * One bit last 1/9600 = 104,16 us * The transmision of one data byte needs 10 bits: one bit start, 8 bits data and one bit stop. The time spent to send one data byte is 10 * 104,16 us = 1041,6 us. * If several consecutive bytes could be received, the uart must be polled at least every 1041,6 us to avoid an overrun error. ::: ## UART frame The figure shows the transmission sequence of a UART according to the following configuration: * 8-bit data, LSB * 1 start bit, 1 stop bit, no parity ![uart](https://hackmd.io/_uploads/HkMPLHFJR.png) What is the value of the transmitted data? :::spoiler ```0x71``` ::: ## UART handshaking :::warning :question: How many wires are normally used for UART null modem communication (crossover cable and no handshaking)? * One cable for transmission and reception * Four wires: TX, RX, ground and clock * Three wires: TX, RX and ground * Two wires: one for transmission (TX) and one for reception (RX). ::: ## UART baudrate accuracy beetween TX and RX Lets have a UART serial line connected to another system with the following configuration: * 1 bit start, 8 bits data, 1 bit stop, no parity * 9600 bauds speed It is requested to analyze the practical tolerances that can be reached in the relative speeds of the TX and RX. From a theoretical point of view the cut-off times for sampling the last bit would be as shown below: ![sampling_uart](https://hackmd.io/_uploads/rJ2yLUoKkg.png) :::warning :question: Assuming the transmitter operates at 9600, what would be the actual allowed speeds for the receiver? ::: :::spoiler * One TX bit last 1/9600 = 104,16 us, for simplificity consider 104 us * A complete frame, with the sampling point in the middle of 8th bit will last 52 + (8 * 104 ) us (884 us). * The time limits are: (8 * 104 = 832 us) when RX speed is faster and (9 * 104 = 936 us) when RX speed is slower * The relative frame time percentages are 94,12% for faster RX baudrate and 105,88% for slower RX baudrate. * Therefore, the accepted RX baudrate window will be: 10.164,48 :: 9600 :: 9.035,52 :::success :raising_hand: It is important to understand that this margin has been calculated assuming that the TX is operating at nominal speed and does not change. In fact this tolerance margin should be applied to the system as a whole. ::: ## TIMER configuration ![Untitled](https://hackmd.io/_uploads/HyurCLYkR.png) Given a SysCLK clock at 100 Mhz and a prescaler value equal to 100: * Select a timer reload value to obtain a periodic tick of 1 millisecond. :::spoiler ```1000``` ::: # Exercises with time measurements and sensors ## Exercise 1 It is desired to obtain the processing time of the function ```uint16_t calculaCRC( uint8_t data[], uint32_t tam );``` which performs the CRC 16 calculation of a sequence of data. You are requested to: Code a program that invokes the function 1000 times with a data sequence of size 256 octets and obtain the average, maximum and minimum processing time. :::spoiler ```c= // Include all necessary code uint16_t calculaCRC( uint8_t datos[], uint32_t tam ); void main() { uint8_t data[256], uint32_t tam = 256; uint32_t i, crc; uint64_t init, end, max, min, ticks; uint64_t sum = 0; float average; for(i=0;i<1000;i++) { init = get_ticks_from_reset(); crc = calculaCRC( data, tam ); end = get_ticks_from_reset(); ticks = (end - init); sum += ticks; if ( i == 0) { max = ticks; min = ticks; } else { if ( ticks > max ) { max = ticks; } if ( ticks < min ) { min = ticks; } } } average = (float)sum/1000.0; } ``` Another version: ```c= // Include all necessary code uint16_t calculaCRC( uint8_t datos[], uint32_t tam ); void main() { uint8_t data[256], uint32_t tam = 256; uint32_t i, crc; uint64_t init, end, max, min, ticks; uint64_t sum = 0; float average; init = get_ticks_from_reset(); crc = calculaCRC( data, tam ); end = get_ticks_from_reset(); ticks = (end - init); sum = ticks; max = ticks; min = ticks; for(i=1;i<1000;i++) { init = get_ticks_from_reset(); crc = calculaCRC( data, tam ); end = get_ticks_from_reset(); ticks = (end - init); sum += ticks; if ( ticks > max ) { max = ticks; } if ( ticks < min ) { min = ticks; } } average = (float)sum/1000.0; } ``` ::: :::success :raising_hand: This is called ```Code Profiling```. Having the execution time of the code labeled is useful to do the system schedulability analysis. ::: ## Exercise 2 We wish to obtain the Round-Trip communication time, i.e. how long it takes from the beginning of the communication until the response is received. ![image](https://hackmd.io/_uploads/rkt5pSXNp.png) You are requested to: Code a program that sends a data via the UART and obtain the time it takes to get the response. Perform this action 1000 times and obtain the average, maximum and minimum Roud-trip time. :::spoiler ```c= // Include all necessary code void main() { int32_t rec; uint32_t i; uint64_t init, end, max, min, ticks; uint64_t sum = 0; float average; for(i=0;i<1000;i++) { init = get_ticks_from_reset(); putchar( 'A' ); do { rec = getchar(); } while( rec == -1); end = get_ticks_from_reset(); ticks = (end - init); sum += ticks; if ( i == 0) { max = ticks; min = ticks; } else { if ( ticks > max ) { max = ticks; } if ( ticks < min ) { min = ticks; } } } average = (float)sum/1000.0; } ``` ::: :::success :raising_hand: What would be the Round Trip time for communications with a spacecraft located on the Moon? :raising_hand: What about Mars? :raising_hand: Find information about the Voyager 1 mission and how long it takes to communicate with the spacecraft. ::: ## Exercise 3 It is desired to obtain the deferred processing time of an ISR. For example, how much time elapses between queuing a TC and starting its processing with the processTC function. You are requested to: Code a program that calculates such time. Perform this action 1000 times and obtain the average, maximum and minimum time. :::spoiler ```c= // Include all necessary code uint64_t init, end, max, min, ticks, i; uint32_t flag = 0; void uart_rx_irq_handler(void){ uint8_t data; data = riscv_getchar(); // save data if ( /* data read end condition */ ) { flag = 1; init = get_ticks_from_reset(); } } int main() { uint64_t sum = 0; float average; //------------- // UART initialization and interrupt enabling //------------- i = 0; // while(1) while( i < 1000 ) { if ( flag ) { end = get_ticks_from_reset(); ticks = (end - init); sum += ticks; if ( i == 0) { max = ticks; min = ticks; } else { if ( ticks > max ) { max = ticks; } if ( ticks < min ) { min = ticks; } } i++; // next iteraction /* data */ } // if } // while average = (float)sum/1000.0; return 0; } ``` ::: ## Exercise 4 We want to estimate the distance to an object. For this purpose we have an ultrasonic sensor that is connected to the GPIO (General Purpose I/O) pins mapped in memory at address 0xFC083000. Specifically Bit 24 is connected to the sensor's terminal that generates the transmission of a pulse and Bit 28 is connected to the sensor's terminal that indicates the reception of an echo. ![imagen](https://hackmd.io/_uploads/Hyq7GcnVp.png) Request: Code a function for RISCV whose prototype is ```double getDistance()``` that returns the distance in cm at which an object is located. :::spoiler ```c= #define INPUT 0 // Input reg #define OUTPUT 1 // Output reg #define DIRECTION 2 // Direction config uint32_t *GPIO = (uint32_t *)0xFC083000; /* - Push buttons BTN[0]: Main reset to the FPGA design BTN[1..]: GPIO inputs [5..7] - Switches SW[0..2] DIP switch: GPIO inputs [0..2] SW[3] Acts as select signal for the UART interface. When "ON" if selects the UART debug link. When "OFF" it selects the console UART 3.3. -LEDs LED[0..3]: Connected to GPIO0 outputs [16..19] */ #define US_CM 29.1 // US per CM #define BIT24 0x01000000 #define BIT28 0x10000000 // El puerto GPIO deberá estar configurado con los bits 19..16, 27..24 como salida, el resto como entrada double getDistance() { uint64_t ticks_ini, ticks; double time_us, distance_cm; GPIOP[OUTPUT] |= BIT24; // Set Bit24 delay( 100 ); // 10 uS delay, MTIME CLOCK=10Mhz GPIOP[OUTPUT] &= ~BIT24; // Clear Bit24 ticks_ini = get_ticks_from_reset(); // Get current MTIME value while ( (GPIOP[INPUT] & BIT28) == 0 ) // Wait while BIT28 is inactive ; // do nothing ticks = get_ticks_from_reset() - ticks_ini; // Total ticks spent // in getting echo time_us = (double)ticks / 10.0; // Ticks to uS distance_cm = time_us / (US_CM * 2); return(distance_cm); } ``` ::: --- There is a problem with the above solution. The function is blocked if the echo is NOT received, either because the object is too far away (out of range of the sensor) or the receiver has simply broken down. You are requested to: Modify the function code so that it waits for the echo signal activation for a maximum of 10 milliseconds. In that case the function will return a value of -1 as error indication. :::spoiler ```c= #define INPUT 0 // Input reg #define OUTPUT 1 // Output reg #define DIRECTION 2 // Direction config uint32_t *GPIO = (uint32_t *)0xFC083000; /* - Push buttons BTN[0]: Main reset to the FPGA design BTN[1..]: GPIO inputs [5..7] - Switches SW[0..2] DIP switch: GPIO inputs [0..2] SW[3] Acts as select signal for the UART interface. When "ON" if selects the UART debug link. When "OFF" it selects the console UART 3.3. LEDs - LED[0..3]: Connected to GPIO0 outputs [16..19] */ #define US_CM 29.1 // US per CM #define BIT24 0x01000000 #define BIT28 0x10000000 #define 10MS_TICKS 100000 double getDistance() { uint64_t ticks_ini, ticks = 0; double time_us, distance_cm; GPIOP[OUTPUT] |= BIT24; // Set Bit24 delay( 100 ); // 10 uS delay, MTIME CLOCK=10Mhz GPIOP[OUTPUT] &= ~BIT24; // Clear Bit24 ticks_ini = get_ticks_from_reset(); // Get current MTIME value while (( (GPIOP[INPUT] & BIT28) == 0) && (ticks < 10MS_TICKS) ) { // Wait while BIT5 is inactive ticks = get_ticks_from_reset() - ticks_ini; } if ( ticks >= 10MS_TICKS) { distance_cm = -1; } else { time_us = (double)ticks / 10.0; // Ticks to uS distance_cm = time_us / (US_CM * 2); } return(distance_cm); } ``` ::: ## Exercise 5 It is intended to generate a square signal using Bit 24 of the GPIO (General Purpose I/O) port described in [Practice 1](https://hackmd.io/@EmbeddedSystems/SE-P1). The timing is as shown below with a fixed [duty cycle](https://en.wikipedia.org/wiki/Duty_cycle) of 50%: ![50](https://hackmd.io/_uploads/BkifVRFR6.png) For this purpose, we already have the following program snippet: ```c= // Other variables, macros // TO COMPLETE void timer_service(void) { // TO COMPLETE } int main() { install_local_timer_handler( timer_service ); local_timer_set_gap( 10000 ); // miliseconds timer enable_timer_clinc_irq(); enable_irq(); // Global interrupt enable // TO COMPLETE IF necessary while(1); // Loop forever } ``` You are requested to: Complete the ISR timer_service ISR to generate a periodic signal with the timing shown in the statement. Declare additional variables and macros as appropriate. :::spoiler ```c= #define INPUT 0 // Input reg #define OUTPUT 1 // Output reg #define DIRECTION 2 // Direction config #define BIT24 0x01000000 uint32_t *GPIO = (uint32_t *)0xFC083000; /* - Push buttons BTN[0]: Main reset to the FPGA design BTN[1..]: GPIO inputs [5..7] - Switches SW[0..2] DIP switch: GPIO inputs [0..2] SW[3] Acts as select signal for the UART interface. When "ON" if selects the UART debug link. When "OFF" it selects the console UART 3.3. LEDs - LED[0..3]: Connected to GPIO0 outputs [16..19] - Bit 24 will be used as output for generated signal */ uint32_t t_high = 5, t_low = 5; // period = 10 // uint32_t t_high = 7, t_low = 3; // period = 10 uint32_t ticks = 0; uint32_t on_high = 0; void timer_handler() { ticks++; if ( on_high ) { if ( ticks >= t_high ) { on_high = 0; ticks = 0; GPIO[OUTPUT] &= ~BIT24; } } else { if ( ticks >= t_low ) { on_high = 1; ticks = 0; GPIO[OUTPUT] |= BIT24; } } } int main(void) { install_local_timer_handler( timer_handler ); local_timer_set_gap( 10000 ); // 10ms tick, CLINT timer: 10Mhz enable_timer_clinc_irq(); enable_irq(); // Global interrupt enable // Enable output on bits 19..16, 27..24 GPIO[DIRECTION] = 0x0F0F0000; GPIO[OUTPUT] = 0x00000000; while(1) // main super loop { // do nothing } return 0; }``` ::: --- Modify the code so that the generated signal is as follows with a fixed duty cycle of 70%: ![70](https://hackmd.io/_uploads/rJlt4CKC6.png) # Exercise 7 Modify exercise 6 so that the duty cycle of the signal can be modified according to the number of times the push buttons on the board are pressed. ![](https://hackmd.io/_uploads/rJ6pLWW6n.png) The operation will be as follows: * At startup the program will generate a square signal with a duty cycle of 50%. * Pressing the first button (BTN0: the rightmost button) will increase the duty cycle by 10% (1 ms) up to a maximum of 90% (9 ms on high and 1 ms on low). * Pressing the second button (BTN1) will decrease the duty cycle by 10% (1 ms) to a minimum of 10% (1 ms high and 9 ms low). :::success :raising_hand: It is only necessary to modify the ```Super Loop``` of the main program. It is about reading the state of the buttons and detecting a rising edge in any of the BTN0/BTN1 buttons. When this happen, the ```t_high, t_low``` variables are incremented/decremented as appropriate, always keeping the 10ms period. ::: :::spoiler ```c= int main(void) { uint32_t last_input, input = 0; install_local_timer_handler( timer_handler ); local_timer_set_gap( 10000 ); // 10ms tick, CLINT timer: 10Mhz enable_timer_clinc_irq(); enable_irq(); // Global interrupt enable // Enable output on bits 19..16, 27..24 GPIO[DIRECTION] = 0x0F0F0000; // Clear LEDS GPIO[OUTPUT] = 0x00000000; while(1) { delay(10000); last_input = input; input = GPIO[INPUT]; if ( !(last_input & 0x10) && (input & 0x10)) { // Rising edge if ( t_high < 9 ) { printf("+"); t_high++; t_low--; } } if ( !(last_input & 0x20) && (input & 0x20)) { // Falling edge if ( t_high > 1 ) { printf("-"); t_high--; t_low++; } } } return 0; } ``` ::: # Exercise 8 Se desea codificar un programa que controle el encendido de una luz mediante un sensor de control de presencia. Las señales generadas se muestran en la imagen siguiente: ![PDI_2025_EXTRA](https://hackmd.io/_uploads/BJk3_cZzge.png) El funcionamiento es el siguiente: * Cada vez que se detecta una presencia se genera un pulso en el PIN4. El bit permanecerá activo hasta que se deje de detectar. * En el flanco de subida del pulso se deberá encender el Led0 conectado al PIN16. * El led se apagará 5 segundos después de que cese la presencia en el flanco de bajada. * Es posible que se produzcan nuevas detecciones antes de los 5 segundos de la detección anterior. El led se apagará siempre 5 segundos después del último flanco de bajada del detector. Para probar el programa en el entorno de laboratorio se simulará el detector de presencia mediante el Botón0 que por defecto está conectado conectado al PIN4 y el Led0 que por defecto está conectado al PIN16. Pulsando el botón se simulará la detección de una presencia. Al pulsar el botón se genera un flanco de subida, la señal permanecerá activa mientras se mantenga pulsado y al liberar el botón se genera el flanco de bajada. ## Se pide: Codificar dos soluciones: * una solución basada en un super loop para el sondeo de la entrada y la detección de los flancos. La medida de los tiempos se realizará mediante la llamada ```get_ticks_from_reset``` * una solución basada en un super loop para el sondeo de la entrada y la detección de los flancos. La medida de los tiempos se realizará mediante la interrupción del timer ## Pruebas Para la verificación del software se realizarán, al menos, las siguientes pruebas: * Pulsar el Botón0, el Led0 se encenderá inmediatamente. Soltar el Botón0, el Led0 se apagará 5 segundos más tarde. * Mantener pulsado el botón durante tiempos indefinidos, desde tiempos muy pequeños a varios segundos. El Led0 siempre encenderá al pulsar y se apagará 5 segundos después de la liberación del botón. * Pulsar el Botón0 y liberarlo. Volver a pulsarlo antes de que venza el tiempo de 5 segundos. El Led0 se encenderá en la primera pulsación y se apagará 5 segundos después de la última liberación. ## Esqueletos de código ### Pooling based solution ```c= #define PIN4 // To complete #define PIN16 // To complete // To complete uint32_t main( void ) { // To complete: variables // Enable output bits uint32_t dir = gpio_get_direction(); dir |= 0x000F0000; gpio_set_direction( dir ); while (1) { // Super loop { // Detect rising edge: ligth on // Detect falling edge: signal ligth off // If light off is signaled and elapsed time is => 5 seconds: light off } } ``` ### Interrupt based solution ```c= #define PIN4 // To complete #define PIN16 // To complete // To complete void timer_handler() { // To complete } int main(void) { // To complete: variables // Enable output on bit 28 uint32_t dir = gpio_get_direction(); dir |= 0x000F0000; gpio_set_direction( dir ); install_local_timer_handler( timer_handler ); local_timer_set_gap( /* To complete */ ); CLINT timer: 10Mhz enable_timer_clinc_irq(); enable_irq(); // Global interrupt enable while(1) // main super loop { // Detect rising edge: ligth on // Detect falling edge: start time down counting // If light off is signaled in ISR: light off } return 0; }```