|
| 1 | +--- |
| 2 | +released: true |
| 3 | +permalink: /labs/lab6/ |
| 4 | +title: 'Lab 6: Drawing into the Framebuffer' |
| 5 | +attribution: Lab written by Philip Levis and Pat Hanrahan, updated by Julie Zelenski |
| 6 | +toc: true |
| 7 | +readings: | |
| 8 | + [Prelab](/labs/lab6/#prelab-preparation) to prep before lab. |
| 9 | +--- |
| 10 | +{% comment %} |
| 11 | +Task list to copy/paste when creating PR for this lab: |
| 12 | + |
| 13 | +__Before releasing lab6:__ |
| 14 | +- [ ] Review writeup/code/checkin questions (instructor) |
| 15 | +- [ ] Walk through (SL) |
| 16 | +- [ ] Followup on issues from previous quarter postmortem (issue #403) |
| 17 | + |
| 18 | +__To prep for lab6:__ |
| 19 | +- [ ] Confirm lab has working monitors with HDMI cables (aim for 1 for every 2 students) |
| 20 | + |
| 21 | +{% endcomment %} |
| 22 | + |
| 23 | + |
| 24 | +## Goals |
| 25 | + |
| 26 | +For your next assignment, you will implement library of simple graphics routines and use them to implement a text console for your shell. The goal of this lab is to review concepts and code in preparation. |
| 27 | + |
| 28 | +During this lab you will: |
| 29 | + |
| 30 | +- Connect your Pi's HDMI port to a monitor and generate video |
| 31 | +- Write code to allocate framebuffer memory and make changes to the pixel data |
| 32 | +- Review C syntax for pointers to multi-dimensional arrays |
| 33 | +- Read and understand fonts and the representation of characters |
| 34 | + |
| 35 | +## Prelab preparation |
| 36 | +To prepare for lab, do the following: |
| 37 | + |
| 38 | +- Bring your Mango Pi and complete parts kit to lab. You do not need your PS/2 keyboard for lab6, it can stay home. (You will use the keyboard for assign6 though) |
| 39 | +- Lab6 and assign6 use an HDMI display. This can be any computer monitor, tv, or projector that accepts HDMI input. |
| 40 | + - Our lab room has a dozen monitors and you are welcome to come to lab anytime to use them. The monitors are to remain in lab for all to share. __Do not take our cables or monitors out of the lab room__. |
| 41 | + - If you have access to an HDMI-capable display elsewhere, give it a try! We have confirmed the reference library's support for HDMI output on a smattering of monitors. We expect it should work on others as well, but are a little wary about possibility of lurking bug or corner case that has yet to surface. Please let us know if you run into trouble and we can help diagnose. |
| 42 | + |
| 43 | +## Lab exercises |
| 44 | + |
| 45 | +### 0. Pull lab starter code |
| 46 | + |
| 47 | +Change to your local `mycode` repo and pull in the lab starter code: |
| 48 | + |
| 49 | +```console |
| 50 | +$ cd ~/cs107e_home/mycode |
| 51 | +$ git checkout dev |
| 52 | +$ git pull code-mirror lab6-starter |
| 53 | +``` |
| 54 | +### 1. Confirm video |
| 55 | + |
| 56 | +Find an available monitor in lab, connect the monitor to power, and turn it on. |
| 57 | +Connect its HDMI cable to the mini-HDMI port on your Mango Pi. |
| 58 | + |
| 59 | +Change to the directory `lab6/grid` and build and run the grid program using |
| 60 | +`make run`. The monitor should display a grid of white lines on a black background. |
| 61 | + |
| 62 | +Everyone should connect their Pi to a monitor and confirm the grid display. |
| 63 | +For the rest of the lab, 2-3 partners can share one Pi/monitor. |
| 64 | + |
| 65 | +### 2. The `fb` module |
| 66 | + |
| 67 | +You will implement the `fb` module to handle allocating the framebuffer memory |
| 68 | +and coordinating with the lower-level `de` and `hdmi` modules to control |
| 69 | +the hardware peripherals that display the framebuffer pixels on the screen. |
| 70 | + |
| 71 | +#### Review code of modules `de` and `hdmi` |
| 72 | +Our reference libmango provides the implementation of the `de` (Display Engine) |
| 73 | +and `hdmi` modules. |
| 74 | +Peruse the module interfaces [$CS107E/include/de.h](/header#de) and [$CS107E/include/hdmi.h](/header#hdmi). You |
| 75 | +'ll note that the modules export a tiny number of functions. |
| 76 | +We wrote those modules to support exactly and only the very specific features needed for our system. |
| 77 | + |
| 78 | +Do a brief skim over the the implementation in files |
| 79 | +[$CS107E/src/de.c](/src#de) and [$CS107E/src/hdmi.c](/src#hdmi). You will not modify this code and |
| 80 | +do not need to make deep dive into it. Your `fb` module will interact with |
| 81 | +the `de` and `hdmi` modules through limited public functions documented in the header files. |
| 82 | + |
| 83 | +#### Initializing the framebuffer |
| 84 | + |
| 85 | +Change to the directory `lab6/fb`. The directory contains these files: |
| 86 | + |
| 87 | +```console |
| 88 | +$ ls |
| 89 | +Makefile fb.c main.c |
| 90 | +``` |
| 91 | +The file `fb.c` contains code to initialize the framebuffer. The `fb` module |
| 92 | +declares a struct to hold the module-level state. This struct has four fields: |
| 93 | + |
| 94 | + |
| 95 | +- `width`, `height`: width/height of framebuffer in pixels |
| 96 | +- `depth`: number of bytes per pixel (always 4 in our system) |
| 97 | +- `framebuffer`: base address of the framebuffer memory |
| 98 | + |
| 99 | +Review the code in the `fb_init` function. Discuss with your tablemates and |
| 100 | +try to answer the questions below. |
| 101 | + |
| 102 | + 1. How are total bytes calculated when allocating the framebuffer? |
| 103 | + 2. What is the color of the pixels in the newly created framebuffer? |
| 104 | + 3. How does the framebuffer coordinate with the `hdmi` module to set the sceen size? |
| 105 | + 4. How does the framebuffer communicate with the `de` module to draw the pixels on screen? |
| 106 | + 5. What kind of changes will be needed for the `fb` module to support double-buffering (i.e. separate front/back buffers)? |
| 107 | + |
| 108 | +If you do not feel confident about your answers or do not agree, please ask us to help resolve! |
| 109 | + |
| 110 | + |
| 111 | +### 2. Draw pixels |
| 112 | +In directory `lab6/grid`, open the `grid.c` file in your text editor and modify the program in the following ways: |
| 113 | + |
| 114 | +1. Change the grid code to draw horizontal lines in red and vertical lines in yellow. |
| 115 | + *Remember from lecture that the B (blue) in BGRA is the lowest byte.* |
| 116 | + |
| 117 | +2. Change the grid code to draw a checkerboard pattern |
| 118 | + (alternating filled red and yellow squares). Hint: define a helper function |
| 119 | + that draws a filled rectangle. |
| 120 | + |
| 121 | +> __Here be dragons__ One critical fact to keep in mind when working with the framebuffer memory is that C does no bounds-checking on array indexes. |
| 122 | +Writing to an index |
| 123 | +outside the bounds of the framebuffer will simply overwrite data at the inappropriate location. |
| 124 | +Such as transgression can create a strange variety of symptoms, including corrupting the framebuffer image |
| 125 | +and/or locking up the pipeline to the display hardware. Should you encounter seemingly bizarre results |
| 126 | +when writing the framebuffer, review how your code accesses its memory. (Note: redzone |
| 127 | +protection can be lifesaver. The malloc module of the reference library has it enabled, if you did not do the extension yourself.) |
| 128 | +{: .callout-danger } |
| 129 | + |
| 130 | +Show off your crisp looking squares to your tablemates! |
| 131 | + |
| 132 | +### 3. Multi-dimensional arrays |
| 133 | + |
| 134 | +Pointers are ubiquitous in systems programming in C and one of the most |
| 135 | +difficult concepts to master. Just when you are getting the hang of pointers |
| 136 | +and one-dimensional arrays, now we're adding multi-dimensioned arrays into the |
| 137 | +mix. The goal of this lab exercise is to review pointers and multi-dimensional |
| 138 | +arrays in preparation for your next assignment. |
| 139 | + |
| 140 | +One convenient way to represent images is with a two-dimensional array. |
| 141 | +Treating it as a 2D array can be easier than explicitly calculating offsets |
| 142 | +into a one-dimensional array. |
| 143 | + |
| 144 | +To start, here is a quick self-test: |
| 145 | + |
| 146 | +* What is the difference between the following two declarations? |
| 147 | + Think about what operations are valid on `a` versus `b`. Also |
| 148 | + think about what memory is allocated. |
| 149 | + |
| 150 | + char *a = "Hello, world\n"; |
| 151 | + char b[] = "Hello, world\n"; |
| 152 | + |
| 153 | +* What is the difference between the following two declarations? |
| 154 | + |
| 155 | + int *p[2]; |
| 156 | + int (*c)[2]; |
| 157 | + |
| 158 | + You may find the **[cdecl tool](http://cdecl.org/)** helpful in demystifying a complex C declaration. |
| 159 | + |
| 160 | +Inspect the code in `lab6/pointers/pointers.c`. Compile the program using |
| 161 | +`make`, run it on your Pi, and interpret the results. Ask questions about |
| 162 | +anything that doesn't make sense to you and be sure your understanding matches the results. |
| 163 | + |
| 164 | +### 4. Fonts |
| 165 | + |
| 166 | +A major part of your assignment will be to draw text on the screen. |
| 167 | +In order to do this, you need a *font*. Each character in the font is a little |
| 168 | +picture that represents the glyph to draw. |
| 169 | + |
| 170 | +{: .zoom .w-100} |
| 171 | + |
| 172 | +This famous font was used by the original Apple IIe. We chose it to |
| 173 | +provide that extra-special retro touch for your graphical console. |
| 174 | + |
| 175 | +Review the file [$CS107E/src/font.c](/src#font). It declares a `font_t` struct for representing a font and |
| 176 | +defines the variable `font_default`: |
| 177 | + |
| 178 | +``` |
| 179 | +/* from font.c */ |
| 180 | +typedef struct { |
| 181 | + unsigned char first_char, last_char; |
| 182 | + int glyph_width, glyph_height; |
| 183 | + uint8_t pixel_data[]; |
| 184 | +} font_t; |
| 185 | +
|
| 186 | +static const font_t font_default = { |
| 187 | + .first_char = 0x21, .last_char = 0x7F, |
| 188 | + .glyph_width = 14, .glyph_height = 16, |
| 189 | + .pixel_data = { |
| 190 | + 0x03, 0x00, 0x33, 0x00, 0xcc, 0x00, 0xc0, 0x3c, |
| 191 | + 0x00, 0x30, 0x00, 0x30, 0x00, 0xc0, 0x03, 0x00, |
| 192 | + ... |
| 193 | + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, |
| 194 | + 0x00, 0x00, 0x00, 0x00 |
| 195 | + } |
| 196 | + }; |
| 197 | +``` |
| 198 | + |
| 199 | +The pixel data for the character glyphs is stored as a bitmap. In a bitmap, |
| 200 | +each pixel is represented by a single bit. If the bit is 'on', the pixel is to |
| 201 | +be drawn in the foreground color; if 'off', the pixel is set to the background |
| 202 | +color. We use a bitmap rather than full RGBA because it takes much less (32 |
| 203 | +times less) memory. This makes the font data much smaller, and hence faster to |
| 204 | +upload to your Pi. |
| 205 | + |
| 206 | +Below is a pictorial representation of `font_default` using green to display |
| 207 | +each 'on' pixel in the bitmap. (click the image to see larger version): |
| 208 | + |
| 209 | +{: .zoom .w-100} |
| 210 | + |
| 211 | +The font bitmap stores the character glyphs in a single line. The leftmost glyph is for the |
| 212 | +character '`!`', whose ASCII value is 33 (0x21), the neighboring glyph is for |
| 213 | +character '`"`' (ASCII 34) and so on ending with rightmost character ASCII |
| 214 | +value 127 (0x7f) Delete, whose glyph is a little checkerboard. The font bitmap |
| 215 | +contains glyphs for 95 total characters, from ASCII 33 to 127. |
| 216 | + |
| 217 | +Each glyph is the same size: 14 pixels wide and 16 pixels tall. This is termed |
| 218 | +a *fixed-width* font. |
| 219 | + |
| 220 | +The bitmap is stored using an array of `uint8_t` |
| 221 | +values. For example, the first two bytes in the array are `0x03, 0x00`. Group |
| 222 | +the 8 bits from the first byte and 6 bits from the second into the 14-bit |
| 223 | +sequence `0b00000011000000`. These 14 bits correspond to the top row of the |
| 224 | +first glyph, the exclamation point. The bits indicate the vertical line for |
| 225 | +the exclamation point is 2 pixels wide and positioned in the center. |
| 226 | + |
| 227 | +Each line of the bitmap is 1330 pixels long (95 characters * 14 pixels wide), |
| 228 | +and requires 1330 bits. If each glyph is 16 pixels tall, how many total |
| 229 | +bytes are in the `pixel_data` array? |
| 230 | + |
| 231 | +Look carefully at the function `font_get_glyph()` in `font.c` which copies a |
| 232 | +single glyph from the font bitmap into a buffer. |
| 233 | + |
| 234 | +* Trace the operation of `font_get_glyph` for ASCII character `&` (ascii hex |
| 235 | + 0x26)? At what locations in `pixel_data` does it look to find the appropriate |
| 236 | +bits? |
| 237 | + |
| 238 | +Now change to the directory `lab6/banner` and review the code in `banner.c`. |
| 239 | +This program calls `font_get_glyph` to store the glyph image into `buf`. The |
| 240 | +code then wants to go on to access `buf` as a 2-d array through the variable |
| 241 | +named `img`, but `img` is missing its declaration and initialization. Read and |
| 242 | +follow the instructions marked `TODO:` to fix this issue. Compile and run and |
| 243 | +you'll get an ascii banner that prints letters to the terminal using your Pi's |
| 244 | +font - neat! |
| 245 | + |
| 246 | + |
| 247 | +## Finishing |
| 248 | + |
| 249 | +### Check in as you go |
| 250 | +Please touch base with us during lab to ask your questions and confirm your understanding. We are here to help, |
| 251 | +let us know of any challenges or confusions we can help resolve with you! |
| 252 | + |
| 253 | +### Submit exit form |
| 254 | +Before leaving, submit the [lab exit form](https://forms.gle/EWGy15v4ySBE4uM17) to let us how the lab went for you and note any issues needing followup. The key goals for this lab are to leave with a good understanding of the layout of the framebuffer memory and feel ready for starting on Assignment 6. |
| 255 | + |
| 256 | +Note that all HDMI monitors and cables must remain in lab for all to use. Do not take them from lab. |
| 257 | + |
0 commit comments