2025-10-07 14:49:54 +02:00

10 KiB

Week 1.5

Assignment 5.1

You will now create a project to run the example program main.c on the STM32F411- E-DISCO development board.
A) Download the project buffer.zip.
B) Import this project into STM32CubeIDE with the menu-option File Import. . . , General Projects from Folder or Archive , Next , Archive , select the file buffer.zip, Open and Finish . Build and debug the project. There should appear some text in the console window. Enter the following priorities: Consumer = 3, Frikandel Producer = 2, and Kroket Producer = 1. You may want to enable the Word Wrap option in the Console window, see Figure 3.

Explain the output of the program. Be precise in your explanation. For example, explain why first all frikandellen are baked followed by all kroketten. Explain why no frikandellen are consumed after the frikandellen producer has stopped. How many snacks are stored in the buffer before the first snack is consumed?

program output:

Enter priority for process Consumer [1..15]: 3
Enter priority for process Frikandel Producer [1..15]: 2
Enter priority for process Kroket Producer [1..15]: 1

Output for Consumer priority = 3 frikandel Producer priority = 2 Kroket Producer priority = 1

Thread: 0x200015a0 starts

Thread: 0x20001b20 with argument: F starts
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\
> FFFFFFFFFFFFFFFFFFFFFThread: 0x20001b20 stops

Thread: 0x200020a0 with argument: K starts

KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\
> KKKKKKKKKKKKKKKKKKKKKThread: 0x200015a0 stops

Thread: 0x200020a0 stops

Thread: 0x200015a0 starts states that 0x200015a0 is the consumer, Thread: 0x20001b20 with argument: F starts states that 0x20001b20 is the frikandel pcroducer and Thread: 0x200020a0 with argument: K starts state 0x200020a0 is the kroket producer.

The consumer start first, because it has the highest priority, but the buffer is empty so it will wait until a producer puts something in it. The thread with the next priority is the frikandel producer. It does not have a delay in it, so it will continue to produce. Meanwhile every systick and when the buffer is full, the consumer takes priority and consumes the buffer. So the frikandel producer can continue to produce frikkendellen.

When the frikandellen are all produced and consumed, there is finaly time for the krokket producer and the same will happen again, but now with krokketten instead of frikandellen.

C) The code of the consumer seems inefficient:

char c = get();
check_errno(sem_wait(&semPrintf));
check_errno(printf("%c", c));
check_errno(fflush(stdout));
check_errno(sem_post(&semPrintf));

The use of the local variable c seems unnecessary. The following code is more compact:

check_errno(sem_wait(&semPrintf));
check_errno(printf("%c", get()));
check_errno(fflush(stdout));
check_errno(sem_post(&semPrintf));

Adjust the consumer code as discussed above. Build the program and debug it with priorities 3, 2, and 1. Explain why the program stalls. Use the FreeRTOS aware Views to help you understand what is going on.

When the consumer wait for the first producer product it is holding on the semPrintf semaphore. This semaphore is also used at the beginning of the producers. Because the consumer has the highest priority it start first, takes semPrintf and wait for the producers. when the producers start they are going to wait for semPrintf, but it is only given free if the procures continue. Now all task are waiting for each other.

D) Compile the (original) program and run it with the priorities: Consumer = 1, frikandel Producer = 2 and kroket producer = 3. Explain the output. Be precise in your explanation. Explain why the kroketten are consumed first. Explain why there are 9 kroketten still consumed after the kroketten producer has stopped. Also explain why there are nine frikandellen still consumed after the frikandel producer has stopped. Only eight of them fit in the buffer! Is the behaviour of the semaphore correct in a real-time environment? Why (not)?

Enter priority for process Consumer [1..15]: 1
Enter priority for process Frikandel Producer [1..15]: 2
Enter priority for process Kroket Producer [1..15]: 3
Output for Consumer priority = 1 frikandel Producer priority = 2 Kroket Producer priority = 3
Thread: 0x200020a0 with argument: K starts
Thread: 0x20001b20 with argument: F starts
Thread: 0x200015a0 starts
KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK\
> KKKKKKKKKKKKThread: 0x200020a0 stops
KKKKKKKKKFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\
> FFFFFFFFFFFFFFFFFFFFFThread: 0x20001b20 stops
FFFFFFFFFThread: 0x200015a0 stops

The kroket producer has the highest priority in this case, so it will start until the buffer if full. Then the frikandel producer will initialise but produce nothing, because it is already full. Then the consumer will start and consume one kroket, then it will be overtaken by the kroket producer. This back and forward will continue until there are no kroketten left. Now the same back and forward will continue, but with the frikandel producer instead of the kroket producer. The first 9 of the products consumed are will kroketten. 8 of them were still in the buffer and one stored in a local variable in the consumer. This is because the consumer halts at the moment the semaphore semEmpty is posted (increased) and this happens in between getting it from the buffer and printing it. When all the frikandellen are all produced the same thing happens. There are also 9 frikandellen consumed after it's producer finished.

E) Try to predict (without debugging the program) what the flow of execution will be when the priorities are changed to: Consumer = 1, Frikandel Producer = 3 and Kroket producer = 2. Test your prediction by running the program.

This is the same as D, but the priorities of the frikandel producer and kroket producer are swapped. So the result will also be the same where F and K are swapped.

F) Try to predict (without debugging the program) what the flow of execution will be when the priorities are changed to: Consumer = 2, Frikandel Producer = 1 and Kroket producer = 1. Test your prediction by running the program.

The producers will run with round-robin, so one after the other. The consumer has a higher priority than the producers, so all will end at the same time.

G) Now finally, try to predict (without debugging the program) what the flow of execution will be when the priorities are changed to: Consumer = 1, Frikandel Producer = 2 and Kroket producer = 2. Think carefully! Test your prediction by running the program.

The producers are again will execute in round-robin, but because the consumer has a lower priority and is not waiting for data in the buffer, the first producer will fill the buffer. After the buffer is filled it will continue the same as F.

Assignment 5.2

In this assignment you are going to replace the buffer with a message queue.

A) Copy the project buffer to buffer_mqueue in the Project Explorer of STM32- CubeIDE. Remove the Debug folder from the buffer_mqueue project, rename buffer.cfg to buffer_mqueue.cfg, and rename buffer.launch to buffer_- mqueue.launch. Open buffer_mqueue.launch and replace all occurrences of buffer with buffer_mqueue. Build and debug the project buffer_mqueueue to verify that it is a correct copy of project buffer.

B) Replace the global variable buffer and the semaphores semMutualExclusive, semEmpty and semFilled with a message queue.

I stharted to remove all the old semaphores, the definition, creation and deletion in main_thread() and the contents of get() and put(char c). Then looked at the examples library and saw #include <mqueue.h> with look like is necessary. Created a global variable to store the queue mqd_t mqFood;. I coppied the initialisation of the queue from the example and chanchd the name and size. and added it to main_thread() just after the semaphores are initialised.

struct mq_attr mqAttrs;
mqAttrs.mq_maxmsg = SIZE;
mqAttrs.mq_msgsize = sizeof(char);
mqAttrs.mq_flags = 0;
check_errno((int)( mqFood = mq_open("food", O_RDWR | O_CREAT, 0666, &mqAttrs) ));

And the corresponding closing code at the end of main_thread()

check( mq_close(mqFood) );
check( mq_unlink("food") );

Then changed the get() and put(char c) function to the following.

void put(char c)
{
    check_errno( mq_send(mqFood, (char *)&c, sizeof(c), 0) );
}

char get(void)
{
    char c = 0;
    check_errno( mq_receive(mqFood, (char *)&c, sizeof(c), NULL) );
    return c;
}

The first test I did the code could not find the definition of O_RDWR and O_CREAT used for mq_open. looking a second time at the includes of the example, I saw #include <fcntl.h> as missing from my code, so I added and it fixed the issue and it builds.

The first run it failed with the check_errno function at the mq_open after reading the source of mq_open the first place it can fail is because of an invalid name (the first argument). I looked back to the example code and saw it has a slash in front of the name, by adding this (both in mq_open and mq_unlink) it fixed the issue and was working.

The final full code is available at /report-2/week_1.5/assignment_5.2/main.c

Assaginment 5.3

In this assignment you are going to replace the semaphore with a mutex.

A) Copy the project buffer_mqueue to buffer_mutex in the same way as described in assignment 5.2 part A.

B) Replace the semaphore semPrintf with a mutex.

I replaced the definition of the semaphore with pthread_mutex_t mutPrintf;, the initialisation of the semaphore with the following

pthread_mutexattr_t ma;
check( pthread_mutexattr_init(&ma) );
check( pthread_mutex_init(&mutPrintf, &ma) );

and replaced all instances of check_errno( sem_wait(&semPrintf) ); with check( pthread_mutex_lock(&mutPrintf) ); and check_errno( sem_post(&semPrintf) ); with check( pthread_mutex_unlock(&mutPrintf) );. as last removed the sem_destroy call.

The final code is available at /report-2/week_1.5/assignment_5.3/main.c

the code work the same as before.