Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed minor typos #17

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/03_bootloader.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Let's call `mkimage` and ask it to create an U-Boot uImage out of the applicatio
u-boot-2018.09/tools/mkimage -A arm -C none -T kernel -a 0x60000000 -e 0x60000000 -d better-hang.elf bare-arm.uimg
```

With that, we say that we want an uncompressed (`-C none`) image for ARM (`-A arm`), the image will contain an OS kernel (`-T kernel`). With `-d better-hang.bin` we tell `mkimage` to put that `.bin` file into the image. We told U-Boot that our image will be a kernel, which is not really true because we don't have an operating system. But the `kernel` image type indicates to U-Boot that the application is not going to return control to U-Boot, and that it will manage interrupts and other low-level things by itself. This is what we want since we're looking at how to do low-level programming in bare metal.
With that, we say that we want an uncompressed (`-C none`) image for ARM (`-A arm`), the image will contain an OS kernel (`-T kernel`). With `-d better-hang.elf` we tell `mkimage` to put that `.elf` file into the image. We told U-Boot that our image will be a kernel, which is not really true because we don't have an operating system. But the `kernel` image type indicates to U-Boot that the application is not going to return control to U-Boot, and that it will manage interrupts and other low-level things by itself. This is what we want since we're looking at how to do low-level programming in bare metal.

We also indicate that the image should be loaded at `0x60000000` (with `-a`) and that the entry point for the code will be at the same address (with `-e`). This choice of address is because we want to load the image into the RAM of our device, and in the previous chapter we found that RAM starts at `0x60000000` on the board. Is it safe to place our code into the beginning of RAM? Will it not overwrite U-Boot itself and prevent a proper boot? Fortunately, we don't have that complication. U-Boot is initially executed from ROM, and then, on ARM system, it copies itself to the **end** of the RAM before continuing from there.

Expand Down
2 changes: 1 addition & 1 deletion doc/04_cenv.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,6 @@ Running `objdump` with the `-t` switch shows the symbol table, so for `arm-none-
600000dc g F .text 00000054 write
```

You can see how smybols defined in the assembly, C function names and linker-exported symbols are all available in the output.
You can see how symbols defined in the assembly, C function names and linker-exported symbols are all available in the output.

Finally, running `arm-none-eabi-objdump -d cenv.elf` will disassemble the code, something that usually ends up being necessary at some point in low-level embedded development.
6 changes: 3 additions & 3 deletions doc/06_uart.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ This is not particularly important for the PL011 UART specifically, which does n

### Initializing and configuring the UART

Let's now write `uart_configure()`, which will initialize and configure the UART. For some drivers you might want a separate `init` function, but a `uart_init()` here wouldn't make much sense, the device is quite simple. The functionitself is not particularly complex either, but can showcase some patterns.
Let's now write `uart_configure()`, which will initialize and configure the UART. For some drivers you might want a separate `init` function, but a `uart_init()` here wouldn't make much sense, the device is quite simple. The function itself is not particularly complex either, but can showcase some patterns.

First we need to define a couple of extra types. For the return type, we want something that can indicate failure or success. It's very useful for functions to be able to indicate success or failure, and driver functions can often fail in many ways. Protecting against possible programmer errors is of particular interest - it's definitely possible to use the driver incorrectly! So one of the approaches is to define error codes for each driver (or each driver type perhaps), like the following:

Expand Down Expand Up @@ -263,7 +263,7 @@ One bit in position `n` can be conveniently written as `1` left-shifted `n` plac

---

Next we configure the UART's baudrate. This is another operation that translates to fairly simple code, but requires a careful reading of the manual. To obtain a certain baudrate, we need to divide the (input) reference clock with a certain divisor value. The divisor value is stored in two SFRs, `IBRD` for the integer part and `FBRD` for the fractional part. Accordig to the manual, `baudrate divisor = reference clock / (16 * baudrate)`. The integer part of that result is used directly, and the fractional part needs to be converted to a 6-bit number `m`, where `m = integer((fractional part * 64) + 0.5)`. We can translate that into C code as follows:
Next we configure the UART's baudrate. This is another operation that translates to fairly simple code, but requires a careful reading of the manual. To obtain a certain baudrate, we need to divide the (input) reference clock with a certain divisor value. The divisor value is stored in two SFRs, `IBRD` for the integer part and `FBRD` for the fractional part. According to the manual, `baudrate divisor = reference clock / (16 * baudrate)`. The integer part of that result is used directly, and the fractional part needs to be converted to a 6-bit number `m`, where `m = integer((fractional part * 64) + 0.5)`. We can translate that into C code as follows:

```
double intpart, fractpart;
Expand Down Expand Up @@ -466,6 +466,6 @@ Unsurprisingly, the driver written in this chapter is not perfect. Some possibil

* Interrupt handling. Currently the driver is being used in polling mode, constantly asking it if new characters have been received. In most practical cases, polling is too inefficient and interrupts are desired. This is something that the next chapter handles.

* More robustness. Error handling, sanity checks and other measures preventing the driver from being incorrectly are good! The driver could return different error codes for different types of receive errors instead of lumping them all together. The driver could keep track of its own status and prevent functions like `uart_write` from executing before the configuration has been done with `uart_configure`.
* More robustness. Error handling, sanity checks and other measures preventing the driver from being used incorrectly are good! The driver could return different error codes for different types of receive errors instead of lumping them all together. The driver could keep track of its own status and prevent functions like `uart_write` from executing before the configuration has been done with `uart_configure`.

* The reference clock is hardcoded as 24 MHz now, the driver should instead query the hardware to find the reference clock's frequency.