Exercises and Research Topics
Click here to go back to the course page.
A designer (in software, hardware, or both) needs to solve
problems. So some items are just exercises (i.e.: "try to explain
this or do that, as you now should be able to") and some are
real-world issues, whether new problems to solve, or a request to
find the rationale behind a choice I made. Both require some
brain-time: thinking of the problem at large and being creative; I don't
care care a lot (or not at all) about the real circuit or the code lines:
what is important is finding the right direction, then the implementation
is not interesting, it's just clock time to be spent, also called
"sweat of the brow" in lawyer speak.
And how can you tell exercises from research
topics? It's simple: if you solved it, it was an exercise,
otherwise it was a research topic -- I found this pearl by Richard
Bellman in The Art of Computer Programming by Donald Knuth.
During the interactive lessons, solutions are provided to some of
the problems, sometimes by students, sometimes by me, usually after
some brainstorming. Solved problems are marked as such: if you missed
the discussion or the solution, please ask me by email. Such
brainstorming and solutions happen in RAP mode ("random access
problems"): they are not boringly solved in the order they are asked:
I want you to think about them, until the time is ripe to
disc{uss,lose} each of them.
Exercises that are not going to be solved, because they are just boring
"study" items, are marked as such.
Table of Contents
The exercises are divided by lesson, where the lesson is where
I proposed the exercise. Each of them may or may not be related to the
subject matter, and sometimes they probe forward to later topics.
01- Git
- 1.1 [study]Read gittutorial(7).
- 1.2 [study]Clone the two repositories related to this course.
- 1.3 [study]Look at the history with gitk or another graphical tool.
- 1.4 [study]Get acquainted with git log as quick alternative to the tool above.
- 1.5 [solved] Guess why commits
57835cb
and 505df81
exist.
- 1.6 [study]Read gittutorial-2(7).
02- PCB design
Here we are looking at the schematic of the TDC board.
- 2.1 [solved] Explain the role of R14.
- 2.2 Explain why R10 is different in value from R11, R12, R13.
- 2.3 [solved] Explain why there are two flash-memory devices: U5 and U9.
- 2.4 [solved] Explain the role of Q201 and R201.
- 2.5 [solved] Explain why R214 should rather be 1k5 than 1k Omhs.
- 2.6 [solved] Explain the circuit with D201, R202, R204, C201.
- 2.7 [solved] Understand the circuit with U8 (U8A and U8B).
- 2.8 [solved] Find suitable values for C8 and C9.
- 2.9 [solved] Explain why I used 1uF and 100nF for C8 and C9.
- 2.10 [solved] Explain why R1 and R2 may be removed (hint: read the LPC11 data sheet).
- 2.11 [solved] Explain why R3 and R4 can not be removed (as above).
- 2.12 [solved] Find a component that doesn't make sense; wonder what its role may be.
03- C language
- 3.1 [study]Read syscalls.S and familiarize with it.
- 3.2 [study]Disassemble (
objdump -d
) hello.o and hell.o and understand what they do.
- 3.3 [study]Ensure you understand argc and argv.
- 3.4 [solved]Try to redesign povacca using data structures.
04- Linker (just an intro)
- 4.1 [study] Add more operations to povacca using the ELF section trick.
- 4.2 [study] Create your own multi-file program using ELF sections, to get experienced.
- 4.3 [solved] Explain why __attribute__((sth)) in gcc takes two pairs of parentheses.
- 4.4 [solved] Find about #pragma and convince yourself that it is a bad choice.
- 4.5 Find about reserved name spaces in C language.
05- Cross compiling
- 5.1 Implement
rdtsc
as shown in the slides (or otherwise)
and try to use it.
- 5.2 [solved] Read
tdc-11-test.c
(commit 10298b8
) and find
the bug and the suboptimal implementation(s); guess what jiffies is
and what udelay does.
06- Versatile timing
- 6.1 [study] Make sure you understand jiffies and regs.
- 6.2 [study] Explain why register addresses are divided by 4.
- 6.3 [solved] Think about "
while (jiffies < j)
" and explain why it is bugged.
- 6.4 [study] Download and run the cross-compiler we built.
- 6.5 [study] Rebuild your own gcc-8.2 binary using the provided script.
- 6.6 Add a configuration for gcc-9 or gcc-10 and verify whether the script fails.
- 6.7 Add a configuration in arm-toolchain to build an AVR
cross compiler.
- 6.8 Test the above compiler with real ATmega code.
07- ARM, GPIO, running on hardware
- 7.1 [study] Disassemble some of our code and try to understand it.
- 7.2 [study] Get acquainted with the pc-relative way to load a constant.
- 7.3 [study] Get acquainted with the call and return instructions.
- 7.4 [study] Try to read the serial port of the test program running on TDC.
- 7.5 [study] Use tools/program and tools/progrom (for the latter,
use the test program binary that is committed in the repo.
- 7.5 [study]Read the gpio header and code and try to understand it.
- 7.6 Suggest a better gpio API than this one.
08- Flash running, initial udelay
- 8.1 [solved] 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).
- 8.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).
- 8.3 [solved] 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.
- 8.4 [solved] 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.
- 8.5 [solved] How can udelay be made better than the .rep/.endr way?
- 8.6 [solved] Implement getc. A simple test
program may just echo back its input, or turn uppercase to
lowercase.
- 8.7 Looking at the uC manual, implement the code to retrieve the
serial number of the processor.
- 8.8 [solved] Make
rot13-code.c
run faster (by making the proper
changes).
09- GPIO and neopixel in practice
- 9.1 [solved] Why is the orange led lit during reset, when the read and green
leds are off?
- 9.2 [solved] Why did I choose exactly those GPIO pins for the diagnostic leds?
- 9.3 [solved] How can we get rid of unused code (e.g. neopixel code in
hello.bin
)?
- 9.4 Write a function that drives 3 neopixels with different colors,
and a test program to actually use it (e.g, make a traffic light).
- 9.5 Make the above function work for n pixels, for any n.
- 9.6 [solved] How could we measure the voltage of a 12V battery if our ADC can
only convert in the range from 0V to Vcc (i.e. 3.3V)?
- 9.7 [solved] Find out why resistor values like 4k7 and 1k5 are so common, whereas
5k0 or 1k4 are almost unheard of.
10- Kconfig and more about time
- 10.1 How would you port the current code base to the AtMega processor? The Kconfig
is already there (though empty), but there are a number of steps to make.
- 10.2 Turn the neopixel example into a data-based application.
- 10.3 [solved] Conceive a better gpio_set() (or __gpio_set(), so the neopixel
code would avoid the cpu-specific writel instruction.
- 10.4 [solved] Make the jiffies variable and operations type-safe (so, for example,
acting on unsigned short or int will fail at build time) without
affecting performance.
- 10.5 Conceive a jiffies_wait() function to make
neopixel-main.c
less ugly -- whether or not you solved 10.2 first.
- 10.6 Draft a TDC application -- i.e. print the timestamp of input pulses. Please
remember that you have two square-wave oscillators available on-board for testing.
11- PLL and printf
- 11.1 [study] Take a look at chapter 2 of the
microcontroller manual to understand the complexity of clock
management. Please remember that this is in the "pretty simple" realm,
as opposed to other current processors.
- 11.2 [solved] Verify that
CONFIG_PLL_CPU
has the desired
effect on CPU freqency. You have no oscilloscope, only a multimeter.
- 11.3 [study] Read the pp_printf documentation and
understand the design choices I made.
- 11.4 Wonder why I used
#ifdef
in pp_printf for
64-bit support instead of relying on weak symbols or other link-time
features.
- 11.5 Allow selection of the pp_printf implementation from
Kconfig.
13- Udelay
- 13.1 Commit bc218dc (udelay test program) shows that
the udelay function has a delay of 3us for each call and is
0.55% faster than expected. Please explain why this happens.
- 13.2 Try to change
CONFIG_HZ
and see the effect on
timing. Try to explain the result and understand why usually
developers run the time-tick at 1kHz, and rarely outside the
100Hz-10kHz range.
- 13.3 [solved] Explain why commit 554ac468 (build-time check) is not acting
on Kconfig, using the "
range
" option.
- 13.4 Add a build-time check to avoid truncation in "
(step /
usec_per_jiffy)
". For example, if HZ is 150 we get 1 instead of
1.5, which make the long delays wrong by 33%.
- 13.5 [solved] Commit ee8e51f (timer loop example) shows how reading time
from two different registers may return wrong results. Pleae suggest
how to get a correct time (some overhead is unavoidable).
14- Processes
- 14.1 Suggest a better way to manage verbosity in commit 5f37fb95.
- 14.2 [solved] Understand the bug marked as such in lib/task.h (in the
last line) and suggest a fix.
15- I/O and sharp timing
- 15.1 In current master,
setup.c
conditionally
calls pll_init, to avoid the call (and the code size in the
binary) if we are not multiplying clock speed. This may happen, for
example, to save power. Unfortunately, the default flash wait states
register is set to 2 (maximum wait states), and we don't want this.
Among other things, it breaks the neopixel code (the previous one,
hand-tuned to 12MHz). Please suggest a fix that doesn't use up
needless code size.
- 15.2 Move led definitions to
board.h
, so the led
example can be board-independent.
- 15.3 Really make a neopixel application (e.g. stoplights). This is
exercise 9.4 again.
- 15.4 Add the neopixel code above to the
r-w-go
interactive
shell.
- 15.5 Add a stack-checking command to
r-w-go
, using
the already-implemented pre-fill with a known value.
18- I2C, SPI, UART
- 18.1 [solved] Now that we discussed what the passive network near ADC pins is,
please suggest a way to increase the capacitors' value when sampling
at a slower sample rate.
- 18.2 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 software driven). Input impedance should be 10kohm. Additionally,
they want current input as an option: 0-20mA over 500 ohms (precision
is unspecified). How would you design your board?
18.3 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. 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.
20- Interrupts
- 20.1 If you look at commit 2ef8f6f5 ("swap operations in putc()")
you see that the diagnostic printout of a single byte still takes
considerable time (almost 1us). Please provide a faster way to send
out a diagnostic byte to the serial port.
- 20.2 Even if 115200 is a common speed, we should move our UART to
run at 230400 to waste less time in transmission. Please provide a
patch to achieve this.
- 20.3 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.
- 20.4 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.
- 20.5 In the lamp situation above, when you make a photograph with
50% lighting (or otherwise), the resulting image is striped, because
image acquisition typically lasts for 40ms, and 78Hz is 12.8ms.
Please 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).
- 20.6 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). The big advantage is that you need no special instruction,
to implement an interrupt handler: you just return.
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 particular CPU setup -- usually an OS just sets up the stack to
return to the target address when it issues "return from interrupt".
- 20.7 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 where it is not.
2- Threaded Interrupts
- 22.1 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.
- 22.2 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.
Alessandro Rubini
Last modified: May 2021