This is an html version of the various exercises-N.txt files that appear in the main page after each lesson.

This is a mix of "exercises and research topics". You may be able or not to solve them, but the important thing is thinking about them. Some of them are related to the specific lesson, and some are generic problems. Some come from real-world problems I faced over the years.

No solution is provided in writing. For all of them, sooner or later, a solution is agreed upon during the lessons.


Exercises-1 (after lesson 02, the "povacca" one)

1- Create a new "povacca" git repository that is a subset of the hsw2020-www repository, only including the povacca code. Note: you are expected to keep the "povacca/" subdir: moving code to the top-level directory is possible but not trivial. Sure you can try if you want.

2- Add you own operations to povacca, but adding one or more op-*.c files, and try to understand why it works, or why it doesn't work.

3- Explain why povacca.h declares "struct pv_oper pv_first[]" as extern, and not "struct pv_oper *pv_first". If you try, please explain why and how it fails. If you don't try, please guess why and how it would fail.

4- Use the elf-section trick, i.e. __attribute__((__section__("sth"))) to create an array of named items, that live in separate source files, and a main program that lists them like this:


    for (....) printf(item->name);

Then, understand why it works. Optionally, first understand why it doesn't work, then fix, finally understand why it works.


Exercises-2 (after lesson 04, the "kicad+tdc" one)

1- Please take a look at the "neopixel" devices (WS2812B or equivalent) and think about the best way to drive a neopixel string from a microcontroller. With or without looking at existing code out there (without looking is more fun, and often more effective too).

2- Imagine you have two microcontroller boards with 3 RGB leds each. They look like a traffic light, so you want to write a traffic light application: You want one device to be red while the other is green and so on. How can you achieve that with one wire only between them? We want the same firmware image in both devices, and we can assume they are powered on at the same time (we can solve anyways, but it's more difficult. If possible, connect the same GPIO pin (i.e. pin X of the first board il shorted the same pin X of the second board).


Exercises-3 (after lesson 05, the "qemu+versatile" one)

1- In order to properly allocate a stack size and check for possible overflows, we should devise a way to check stack usage. Suggest a way to track stack usage and/or detect overflow.

2- boot.S is "ugly", as we would like to have everything in C. Is it possible to replace it with boot.c? Try to write boot.c and evaluate why it does or doesn't work.


Exercises-4 (after lesson 06, the "running on real hw" one)

1- Why, in the TDC board, the orange led is partially lit while the green and yellow ones are completely off?

2- Knowning that C8 and C9 are 100nF and 10nF, respectively, please calculate the frequency of the two square waves that are output to the test strip J11.

3- In the first commit of the hello program, explain why we see "Hello, once aga" instead of seeing H alone, or a random selection of bytes within the string.

4- setup.c is currently hosting the timer initialization. Over time, we will need to add more initialization, for unrelated peripherals. Putting then all together in the same file is ugly, bceause we want drivers to remain separate one another. How would you solve the problem?

5- Despite the 12MHz clock oscillator, we found that the timing of hello.bin is very jittery. Can you guess why?

6- Can you find a proper fix for the overflow of jiffies?

7- How can udelay be made better than the .rep/.endr way?

8- Next thing we need is access to the pins (GPIO = general purpose input/ouput). All pins have alternate functions and we need an API for both setting the alternate function and reading/writing the pin. If you read the LPC manual, you find that for some ping the "alternate function 0" is not GPIO, but another function. How would you design your API for GPIO access, in this situation?


Exercises-5 (after lesson 07, the "flash and kconfig" one)

1- Let's imagine your client wants you to make a PCB with an ADC device. They want 12 bits of precision for an input range of 0-5V, 0-12V, 0-24V, selectable with some micro-switch of your choice (better if if software driven). Input impedance should be 10kohm. Additionally, they want a current input: 0-20mA over 500 ohms (precision is unspecified). How would you design your board?

2- Remember that putc, as currently implemented in out code base, is suboptimal. I'm still waiting for suggestions about how to make it better.

3- When things go bad we want to panic the system (i.e., hard lock). At least, I want this, if you disagree let's discuss about it. The point is that if something goes wrong in lab, it should not get unnoticed, and if something goes wrong when your client is testing before deployment, they must be able to report you. So, with your TDC board, please design a panic API and describe the implementation.

4- In the TDC we have a 12MHz oscillator, and lib/pll.c relies on this. But what about boards without an oscillator? The internal RC oscillator is enough for many use cases: a PCB without oscillator saves real estate and real money (say 0.5 EUR per board). How do you support this in software?

5- Please add to the current code-base full support for Cortex-M3 processors (e.g.: LPC1347). Some of those microcontrollers have the same footprint, so we can mount a TDC with them. Yes, I have one.

6- Support AVR (the Arduino family). Stuff is half-done already, with the Kconfig infrastructure. Please describe how would you complete such support in the current code base.

7- The default number of flash wait states is the maximum possible. But if we run slower (e.g. at 12MHz, with PLL=1), we can configure the flash for no wait states (the manual says we need 1 wait state every 20MHz of CPU speed). Please suggest an implementation to optimize this.

8- Study the LPC UART and support 230400 baud (not "funny", but you must be able to read the data sheet, besides, the code is almost there).

9- One of my TDC boards mounts a 10MHz VCXO. Currently the code requires a 12MHz oscillator. Please describe the steps to support 10MHz.


Exercises-6 (after lesson 08, the "gpio and printf" one)

1- As requested in the video, please suggest changes to the better in the GPIO API I committed. There are suboptimal details (try to use it yourself, you will note).

2- Assuming the new API is incompatible, please suggest a way to migrate from the current API to the new one. Imagine you have several users of your current API, for example by replicating this include/ and lib/ in other packages (that want to track this "uspstream"), or that you have dozens of applications in this very package (the two situations are different, and the best approach differs).

3- Explain why the three leds in the TDC board are connected to ping 0_1, 1_28 and 1_31.

4- Suggest a way to add "external" GPIO pins (e.g. an I2C extender, but that's irrelevant. What I want is an API to plug new GPIO pins that are then accessible with the standard API (esp. gpio_set() and gpio_get()).

5- Now that we have pp_printf, we really want to select among the several implementations, instead of just using vsprintf-full.o. Please fix the code base to do that.

6- With current code, we can use time_after() with jiffies, but we could still do the buggy comparison "j < jiffies". Please suggest a way to force a build failure whenever the user touches jiffies as an integer, to force consistent use of accessor functions (time_before(), but also a new jiffies_add() that must be provided).

7- Why does tests-tdc/hello include GPIO code, even if it doesn't reference GPIO and we use -gc-sections?


Exercises-7 (after lesson 09, the "processes" one)

1- In the leds-tdc example, we find that the orange led is really too bright. We could replace the 1k5 resistor with 4k7, but it might be possible to make it less "loud" without using the soldering iron. Make you proposal. Unfortunately there is no feasible solution for the orange led, but other pins are able to do that -- not the other leds, either, unfortunately.

3- Think about memory management in a microcontroller (malloc/free or lack thereof) and evaluate what would be the best approach and API.

3- Figure how to measure the WCET of a task.

4- Imagine you have problems with a PCB that you designed. The prototypes were good but production items (different manufacturerer) fail for thermal issues, and you feel with your finger that copper is thinner than the prototypes. You need to check that it the copper thickness is really 35um (and not, say, 20um).

You can cut the PCB, actually you brought home a fragment (see a real sample of the device here). How would you measure the thickness of the copper?

5- Think about a filesystem for microcontrollers. The code must be as small as possible (4kB of machine code, or 2kB, or even 1kB if possible), we need to access files by name, write support is useful (but a filesystem without write support is better than no filesystem), and if we write we must respect the block-size of the hosting hardware (e.g. 32b/64b for eeprom or 64k/128k for bigger flash memory). How would you design the filesystem?

6- What is suboptimal in the "struct task" as it appears in the slides? Here it is again:

    struct task {
        char *name; void *arg;
        int (*init)(void *arg, struct task *t);
        int (*job)(void *arg, struct task *t);
        unsigned long nextrun, period;
    };

7- What is the bug in the scheduler as it appears in the slides? Here it is again:

    while (1) {
        for (best = t = task_first; t < task_last; t++)
            if  (time_before(t->nextrun, best->nextrun)
                best = t;
        while (time_before(jiffies, best->nextrun)
            ;
        best->job(best->arg, best); /* and possibly manage retval */
        best->nextrun += best->period; /* BUG! */
    }

8- Looking at lib/task.c and include/task.h, you'll find how I handle diagnostic messages (the "verbose" argument). It's bad in my opinion, can you make it better?


Exercises-8 (after lesson 11, the "interrupts" one)

1- Describe and/or design a lockless circular buffer. It's irrelevant whether you use it for output or input data streams. You must be able to detect overflow and underflow, know how much data was lost, and neither the reader nor the writer should ever block waiting for the peer -- or stop execution of the peer.

2- Imagine you have a single-interrupt system that drives a number of PWM signals: with a 20kHz interrupt we drive 256 light levels for each lamp, thus acheiving 78.1 Hz in the output.

This is the code, in the periodic interrupt:


   for (lamp = 0; lamp < ARRAY_SIZE(lamps); lamp++) {
       if (step == 0)
           gpio_set(lamps[lamp].gpio, 1);
       if (step == lamps[lamp].pwm)
           gpio_set(lamps[lamp].gpio, 0);
   }
   step++; step &= 0xff;

Please find the important and the trivial bug in the code above, and fix them.

3- In the lamp situation above, when you make a photograp with 50% lighting (or otherwise), the resulting image is striped, because image acquisition lasts for 40ms and 78Hz is 12.8ms. Plesae suggest a way to attack the problem (whether or not the issue can be solved depends on several factors; sure an analogue camera with a real shutter and photographic film wouldn't be affected).

4- In the Cortex-M0, processor status is pushed on the stack and interrupts are disabled until you "return" to special address 0xfffffff9, and that special return address forces the stack to be popped. See page 447 of the user manual for details about the stack frame.

This is different from most other processors, where you can touch special CPU status registers to "leave" interrupt mode and jump to your own code (e.g., a new task that preempted what was running before).

Please figure how you can implement preemption (i.e. a context switch) and return to the previous task (i.e. the other context switch) with this strange CPU setup.

5- Imagine you are writing your scheduler. Please design diagnostic tools to help you and your mates undestand that the scheduler is working as expected, or that it does not. Please consider both situations where an oscilloscope is available, and those where it is not.

6- We saw how the square wave application written by the main loop stops where the interrupt fires, and saw on the oscilloscope that it takes slightly more than 1us to execute. How would you measure this time if you lacked an oscilloscope? The figure is here.

Square wave on the scope

7- And how would you measure the duration of the interrupt handler itself if you lack an oscilloscope? This is the cyan oscilloscope trace in this image.

Square wave and irq

Exercises-9 (after lesson 17, the "irq-thread" one)

1- Write an i2c driver, to access the eeprom we have in the tdc board. It can use the hardware support in the microcontroller, or just rely on gpio pins.

2- Identify where is the race condition in commit 02fa9a5f. This is the alternative __sched implementation that I then reverted from the repository.

3- Use the leds in the mini-preempt application in order to actually "see" preemption: one led for the fast task, one led for the slow task, and one for the main function, that must turn off when preemption happens.

4- Describe how you can use the "timestamp_fraction" argument to the interrupt handlers to get interrupt timestamps. Please remember that a timestamp has two components: the jiffies value and the fractional counter.

5- If the system is using preemption with a number of tasks, use of the stack is fuzzy, because the various tasks will overlap in unpredictable ways. You are not sure about stack use in production, despite your tests during development. Please find a way to measure the worst case use of the stack, to validate you project before deployment.

6- Back to malloc. Sometimes tasks may need temporary storage, but it is released before the iteration of the state machine (i.e. before the job returns). If more than one task needs temporary storage at each iteration, it may make sense to allow sharing, through malloc/free (but if we have preemption the stack problem above surfaces again. Please draft or write a malloc implementation suitable for this use case. Also, find a way to ensure no out-of-memory will ever happen.

7- Let's imagine you are asked to make a relay board, or an array of smaller boards. Your client wants to drive 50 low-power lamps, but you fear the number will increase later: all clients are the same in this respect. It's just on-off, no PWM is needed.

Operation is mostly unattended, and it's paramount that it works for a whole week. If some component fails, it should be quickly replaceable by non-technicians, and you are especially concerned about the solid-state relays -- no audible ticking is allowed at the deployment site. Please lay out the ideas for your implementation.


Exercises-10 (after lesson 22, the "filesystems" one)

1- A thermocouple delivers roughly 1mV every 25 degrees of delta-T, i.e. 40uV/deg. The internal ADC of the microcontroller is a 12 bit device, GND-to-Vcc: this means slighty better than 1mV per step, on a 3.3V power supply. Draw the circuit and write the code to be able to sample a thermocouple using the internal ADC (hint: we might use an LM324 amplifier). This was discussed during lesson 21.

12- Write the I2C driver for the microcontroller's logic block. Now that we have an I2C framework (with struct i2c_operations), it's easier than it earlier was.

13- Augment the shell with commands to read/write spi-nor and i2c-eeprom devices. This is also needed as a test environment for the driver code

14- Find a better way to support IAP memory than removing 32 bytes of RAM. Ideally, if the application is not using IAP, it doesn't need to reserve RAM for those routines.

15- Connect the three storage engines we already have (spi-nor, eeprom-i2c, eeprom-lpc) to the storage.h idea, adding an API (e.g. storage_register()/storage_unregister()). If more fields are needed in the storage structure, they should be added.

16- Augment the shell with test commands for the above generic storage.

17- Think about a filesystem API, that can support several back-ends

18- Implement a TLV-based filesystem based on the above


Alessandro Rubini
Last modified: Apr 2020