[SOLVED] Comp2300 Lab 6

35.00 $

Category:
Click Category Button to View Your Next Assignment | Homework

You will receive the following solution file(s) instantly after successful payment:

zip file icon comp2300-2021-lab-6-ie8la1.zip (93.4 KB)
Assignment Instructions Updated Recently? Submit Below and we will provide new Solution!
Submit New Instructions
πŸ”’ Securely Powered by:
Secure Checkout
5/5 - (3 votes)

Before you attend this week’s lab, make sure:

  1. you can read and write basic assembly code: programs with registers, instructions, labels and branching
  2. you have read the laboratory text below

In this week’s lab you will:

  1. learn how to load and store withΒ ldrΒ andΒ str
  2. blink the LEDs by reading & writing special hardware registers
  3. useΒ functionsΒ to structure your code and remove duplication

Here’s a table of contents for this week’s lab, just in case you need to jump around.

Introduction

In this week’s lab you’ll read and write some of the special hardware registers on your discoboard to seeΒ LEDΒ β€œoutput” from your program. Seeing stuff happen in the real world is a big part of the fun of microcontrollers, so this is going to be fun.

You’ll also start to see more clearly the connections between what we’ve been covering in this course and the higher-level programming languages you’re used to, with theirΒ ifΒ statements,Β forΒ loops, and other niceties. This process of β€œdemystifying” programming is a big part of what this course is about, so take the time to reflect on what you’re doing and how it fits in with what you know and do in other programming situations.

Exercise 0: bit shifting practice

Plug in your discoboard, fork & clone theΒ lab 6 templateΒ to your machine, and open theΒ src/main.SΒ file as usual.

The stuff in Exercise 0 is all stuff we’ve covered already (in some cases as far back as week 2) but it’s sometimes good to have a warm-up/refresher. Feel free to jump straight toΒ Exercise 1Β if you’re feeling adventurous, but there’s no shame in taking your time and working through the warm-up stuffβ€”practice makes perfect!

Bit-shifting & logic ops

You will need to use some logic operations in this lab, in particular, setting (setΒ 1) and clearing (setΒ 0) bits. This warm up exercise gives you a chance to practice bit shifting and using logic operations to set and clear bits.

Edit yourΒ main.SΒ file so that it looks like the following. Don’t forget the requiredΒ .syntax unifiedΒ andΒ .global mainΒ statements at the top of the file.

main:
  ldr r0, =0xcafe0000
  ldr r1, =0xffff

  @ your code goes here

@ when it's all done, spin in an infinite loop
loop:
  nop
  b loop
Decimal
Hex
Binary

UsingΒ onlyΒ the instructions in theΒ LogicΒ andΒ Shift/RotateΒ subsections of the cheat sheet (but as many registers as you need) write a program which putsΒ allΒ of the following values into the listed registers. Use the cheat sheet and the converter widget to help you outβ€”draw β€œbit pattern” pictures on a piece of paper if it helps.

  1. 0xcafeffffΒ intoΒ r3
  2. 0xcafeΒ intoΒ r4
  3. 0xcaff0000Β intoΒ r5
  4. 0xc0fe0000Β intoΒ r6

These shouldn’t require heaps of codeβ€”just a couple of instructions for each. Remember theΒ stuff you’veΒ done inΒ previous labs.

Did you needΒ bothΒ of the β€œstarting” values (e.g.Β 0xcafe0000Β andΒ 0xffff) or could you have got the job done with only some of them? Which ones are essential?

Can you do each task with only one instruction each?

Exercise 1: using labels, ldr, and str

Labels and loading arbitrary numbers into registers

Labels (Page 24)Β are the symbols in your source code followed by a colon (:), e.g.Β main:. You’ve probably already got an intuitive feel for how they work: you put them in your code wherever you like, and when you want to branch to that part of the program you put the label in as the β€œdestination” part of the branch instruction. Here’s an example:

loop:
  @ do stuff

  b loop @ branch back to the "loop" label

In theΒ week 3 labΒ you even used conditional branches to only branch under certain conditions (i.e., if certain flags were set). And we covered this in theΒ week 4 lecturesΒ as wellβ€”including how to turn β€œmathematical” conditional expressions (e.g.Β x >= -45) into sequences of assembly instructions.

But what are labels, really? Add this code to your program (under theΒ mainΒ label):

ldr r0, =main

After you step through this line, what’s inΒ r0? You might be wondering what theΒ =Β sign is doing in your program. Remember from your week 2 lab that instructions are stored in memory with various encodings (some are 16-bit, some are 32-bit) and that when you use anΒ immediate valueΒ constant (e.g.Β 42) in an instruction which supports it then the bit pattern forΒ 42Β (which isΒ 0b101010) is storedΒ insideΒ that instruction.

This means that if you need to include a constant which is 32 bits long (e.g.Β 0xABCD1234) then you can’t fit it in the instruction. You may have run into this problem alreadyβ€”the error message will be something like

Error: invalid constant (ffffffffabcd1234) after fixup

and what it means is that the constant value you’re using is too big (too many bits) for the instruction you’re trying to fit it inside.

If you’re interested in how exactly the ARM instruction set deals with this problem, and which constantsΒ canΒ be stored inside a 32-bit instruction, thenΒ here’sΒ an interesting blog post. Recall we use the Thumb-2 instruction set, so it’s not exactly the same on the discoboard (see section A5.3.2 in the reference manual for how they work on the discoboard).

Instead, you can play with the following widget to see how our board encodes a shift. The top input is the 12-bit encoded value. The second input is the 32-bit expanded output. Below this, the longer table is the binary representation of the output. An orange colour represents one of the 4 β€œspecial” shifts, while blue means it is a regular shifted byte. Similarly, the shorter table shows the parts of the encoded input, with the shift in purple and the data in green. Note how bit 7 of the encoded input is conditionally part of the shift or data, depending on bits 10 and 11. Each bit in the binary table can also be toggled by clicking it.

313029282726252423222120191817161514131211109876543210

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

11109876543210

0 0 0 0 0 0 0 0 0 0 0 0

Because this is a bit of a pain, the assembler provides a special syntax for storing larger values in registers. It’s based around theΒ ldrΒ (load register) instruction, and if you prefix the constant with anΒ =Β sign then the assembler will generate the code to load the full value into the register, as we discussed in theΒ week 4 lectures.

Have a look at these two lines of assembly code:

mov r0, 0xFFF
ldr r0, =0xFFF

will they result in the same assembly instructions when uploaded & running on your discoboard? How might you check? Hint: the disassembler is your friend 😊.

Can you think of anyΒ otherΒ lines of assembly code (apart from the two above) which will be assembled into the same machine instruction(s)?

So how does this relate to theΒ ldr r0, =mainΒ instruction above? Well, the answer is that the labels in your program are just valuesβ€”they’re the addresses (in your board’s memory space) of the instruction which occurs after them in the program. After the linker figures out exactly which address each label points to, it β€œreplaces” them in the program, so that

ldr r0, =main

becomesΒ something like

ldr r0, =0x80001c8

or whatever address theΒ mainΒ label ends up pointing to (which will change every time your program changes).

And sinceΒ 0x80001c4Β (or whatever it is) is just a bit pattern in a register, you can do the usual arithmetic/logic stuff you can do with any values in registers:

Write a small program which calculates the size (in memory) of theΒ movs r3, 1Β instruction and stores the result inΒ r0.

Exercise 2: the load-twiddle-store pattern

TheΒ load-twiddle-storeΒ pattern is a fundamental pattern in making your discoboard do useful work. The basic idea is this:

  1. load some data from memory into a register
  2. operate on (β€œtwiddle”) the value in the register (e.g. with anΒ addΒ orΒ andΒ instruction)
  3. store this new value from the register back into memory

load-twiddle-store

If you’ve forgotten about β€œdata” and β€œtext” sections, you can review it in theΒ week 5 lab content.

Let’s now make use of a data section to store some (spoilers) data, and attempt to load-twiddle-store.

main:
  ldr r1, =storage
  @ your code starts here

.data
storage:
  .word 2, 3, 0, 0 @ don't change this line

Starting with the code above, use theΒ load-twiddle-storeΒ pattern to change the first four data words toΒ 2Β 3Β 0Β 1Β instead ofΒ 2Β 3Β 0Β 0. Hint: first load theΒ storageΒ label using theΒ =Β instruction, then remember that you canΒ load and store with an offsetΒ from this base address (check the cheat sheet). You’ll probably also want to use the memory browser view (like you didΒ in week 2) to watch the values change in memory.

Exercise 3: hello, LED!

So what does all that stuff have to do with blinking the LEDs? Well, the answer is that there’s a section of the discoboard’sΒ address space (Page 23)Β (0x40000000Β toΒ 0x5FFFFFFF) which is mapped to peripherals (as shown in the picture above). To interact with the LEDs, LCD, microphone etc. on the board you need to talk to the hardware by reading and writing to special memory locations in this memory range. To figure out exactly which addresses are mapped to which peripherals, you need to look at the discoboardΒ reference manual

discoboard with LEDs lit up

One type of peripheral is aΒ General Purpose Input/OutputΒ pin. You can see them on your discoboard as little gold-coloured spikes sticking up out of the top and bottom of the board. Your discoboard has lots of them, and you can wire them up to other devices to make more sophisticated devices.

This exercise is pretty long, so here are the steps you’ll go through to turn on the LED:

  1. enable the clock for correct LED GPIO pin
  2. set the pin to output mode
  3. set a bit in the pin’s data register to turn the LED on

Don’t worry if you don’t understand some of those termsβ€”the rest of this exercise will explain all the details.

Some of the ports on the discoboard are already connected to certain bits of hardware on the board. In theΒ discoboardΒ userΒ manualΒ Section 7.5Β User interface: LCD, joystick, LEDsΒ it says:

  • LD4 user: the red LED is a user LED connected to the I/O PB2 of the STM32L476VGT6
  • LD5 user: the green LED is a user LED connected to the I/O PE8 of the STM32L476VGT6

The first bullet point says that the red LED is connected to GPIO pinΒ PB2. This means that it’s connected toΒ pin 2Β ofΒ port B. Just a note that theΒ userΒ manual (short) is different from theΒ referenceΒ manual (long & detailed).

What port+pin do you think the green LED is connected to?

The GPIO pins are grouped into 8 ports (port A to port E) and each port has 16 pins (pin). It’s worth pointing out that the pin numbering starts at 0, so the first pin in port A is PA0.

Ports and pins

From theΒ reference manual:

Each general-purpose I/O port has four 32-bit configuration registers (GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR and GPIOx_PUPDR), two 32-bit data registers (GPIOx_IDR and GPIOx_ODR) and a 32-bit set/reset register (GPIOx_BSRR).

Don’t worry if this seems overwhelming, the main point is that each of these ports has a few dedicated configuration registersβ€”some are used to turn it on, some are used to set it up, and some are used to receive data (read the voltage on the pin as 0 or 1) or send data out (set the voltage to high or low). When it saysΒ GPIOx, that means that there exists a version for all the ports, so the port A version would be calledΒ GPIOA, etc.

These are not like the CPU registers you’ve been using so far (e.g.Β r0Β orΒ r5). Instead, they’re mapped to certain parts of the address space (so they’re sometimes called memory-mapped registers). Read/write access to this register happens through load/store instructions to a specific memory address (as with pretty much everything in aΒ load/store architecture (Page 27).

The register for turning on the clock (step 1) is in theΒ Reset and Clock Control (RCC)Β section of the address space, which on your discoboard starts at memory addressΒ 0x40021000. The specific register which controls the clock for GPIO ports is theΒ RCC_AHB2ENRΒ 32-bit register, which lives at an offset ofΒ 0x4CΒ from the RCC base address and looks like this:

RCC_AHB2ENR register

As you can see, the clock for GPIO port B (where your red LED is) is controlled through bit 1 (i.e. theΒ secondΒ bit from the right, because the rightmost bit is bit 0). You can have a look at Section 6.4.17 of the reference manual for all the gory details.

Why do we need to turn on a clock to even use the GPIO pins? Why aren’t they just permanently enabled to save us all the hassle? Hint: the discoboard is marketed as an β€œultra-low-power microcontroller”. It’s designed to support being on battery power for long durations without recharging.

Note that in debug view you can conveniently see this information in theΒ PeripheralsΒ pane:Β Peripheral View

This is your chance to see theΒ load-twiddle-storeΒ pattern from Exercise 1 in action. To turn on GPIO port B, you must:

  1. load: load the RCC base address into a register, then do an β€œoffset” ldrΒ with theΒ RCC_AHB2ENRΒ offset to read the current state of theΒ RCC_AHB2ENRΒ register into a CPU register
  2. twiddle: use bitwise operations to set the second-from-the-rightΒ GPIOBENΒ bit to 1 while leaving the other bits unchanged
  3. store: write the newΒ RCC_AHB2ENRΒ value back to the memory address you read it from earlier

When we talk aboutΒ settingΒ a bit, that means that it should be equal toΒ 1, andΒ clearingΒ a bit means it should be equal toΒ 0.

So what does the code to perform these load-twiddle-store steps look like?

@ load r1 with the base address of RCC
ldr r1, =0x40021000

@ load r2 with the value of RCC_AHB2ENR
@ (note the 0x4C offset from the RCC base address)
ldr r2, [r1, 0x4C]

@ set bit 1 of this register by doing a logical or with 2
@ think: why does this work?
orr r2, 0b10

@ store the modified result back in RCC_AHB2ENR
str r2, [r1, 0x4C]

Why is theΒ loadΒ part of this process necessary? Why can’t you just store aΒ 2Β into theΒ RCC_AHB2ENRΒ register and be done with it?

You can paste the above code straight into your program. There are a couple more steps before you can actually turn the LED on, but they’re basically the same load-twiddle-store pattern, except with different addresses and β€œtwiddles”.

Now it’s your turn: copy-paste a second copy of the code above as a starting point, but you’ll need different load/store addresses and different twiddles. To set pin 2 of GPIO port B to output mode, you need to set theΒ MODE2Β bits (4 and 5) of the GPIO mode registerΒ GPIOB_MODER, which lives at an offset ofΒ 0x0Β from the GPIO base address ofΒ 0x48000400Β (see Section 7.4.1 of the manual for more info). Here’s what theΒ GPIOB_MODERΒ looks like:

GPIOx_MODER register

To configure pin 2 for output mode to power the LED, you need to ensure the mode bits for pin 2 (MODE2Β in the diagram) areΒ 01Β for output mode (i.e. clear bit 5, set bit 4).

This output mode configuration for the mode register bits is sufficient for turning the LEDs on and off, but there are many more ways to configure the GPIO pins. If you’re interested, have a look in Section 7.4 of the manual.

Your PB2 pin is now configured and ready to roll. The only thing left to do is to actually send an β€œon” signal to it by setting aΒ 1Β into bit 2 (for pin 2) of the port B Output Data RegisterΒ GPIOB_ODR, which lives at offsetΒ 0x14Β from the GPIOB base address and looks like this:

GPIOx_ODR register

Following the steps above, write a program which turns on the red LED on your discoboard, and push it up to your fork on GitLab.

The red and green LEDs are normally near the top left corner of the reset button. If you look closely, you can see the labelsΒ LD4Β andΒ LD5Β on the board there. The LEDs above the LCD screen are not the ones we are working with.

There are a few fiddly things which can go wrong here. If your LED isn’t coming on, talk with your neighbour/tutor about your program. Have you accidentally set the wrong bit (remember that the ports and bits are 0-indexed, so the rightmost bit is bit 0, not bit 1). Are you reading the existing register value correctly? Are you turning the bit on correctly? Are you writing it back to the right memory address? Step through the program with your partner to see what might be going wrong.

Once you can turn the red LED on, add code to turn the green one (PE8) as well. Commit & push your red+green program up to GitLab.

Exercise 4: functions review

Hey wow! You made it toΒ Exercise 4Β which is about theΒ halfway pointΒ for this lab! As we have both week 6 and week 7 for this lab, you can feel free to stop here and pick it up in week 7. Alternatively, if you’re preparing for week 6’s lab, make sure you understand everything up to this point. Of course if youΒ wantΒ to keep going, I’m not going to stop you! Have fun! πŸ˜€

You should now feel a warm glow of satisfactionβ€”let there be light! But you’ll also notice that a few of the steps you had to go through were pretty repetitive. For every step you just did, you were really doing one of two things:

  • settingΒ a specific bit at an offset from a base address,Β or
  • clearingΒ a specifc bit at an offset from a base address

Wouldn’t it be good if we could β€œfactor out” the common parts of those two tasks, so that the code is simpler and clearer? We can do that withΒ functionsΒ (something you should remember from last week). We’re going to take this quite slow, if you’re feeling confident–you canΒ skip to the second β€œpush” boxΒ and work from there.

set_bit_0x48000400_0x14_2:
  @ code to set bit 2 of word at offset 0x14 of 0x48000400
  bx lr

main:
  @ ...
  bl set_bit_0x48000400_0x14_2
  b main

Update yourΒ red+greenΒ program by writing a few functions using the name patternΒ set_bit_<base>_<offset>_<index>Β andΒ clear_bit_<base>_<offset>_<index>Β following the example above, so that yourΒ mainΒ function is just a series ofΒ blΒ instructions (and β€œspins” in an infinite loop at the end).

Arguments/parameters

You’ve now modularised your code (broken it up into smaller, re-usable parts), but it’s still pretty repetitive. There’s a lot of repeated code between theΒ set_bit_xxxxΒ functions.

The only difference between these repeated versions is the difference in inputs. Therefore we can passΒ argumentsΒ to functions to parameterise those functions, so that we just have oneΒ set_bitΒ function that we call with different β€œinputs”.

As we learnt last week, we leave values inΒ r0–r3Β before callingΒ blΒ to act as β€œinputs” for our functions. Consider the followingΒ sum_x_yΒ function:

main:
  mov r0, 3   @ first argument, x
  mov r1, 2   @ second argument, y
  bl sum_x_y  @ call sum_x_y(3, 2)
  @ get result back in r0

.type sum_x_y, %function
@ args:
@   r0: x
@   r1: y
@ result: r0
sum_x_y:
  add r0, r1
  bx lr
.size sum_x_y, .-sum_x_y

The function adds the values inΒ r0Β andΒ r1Β and puts the result inΒ r0. So the values inΒ r0Β andΒ r1Β areΒ argumentsΒ (orΒ parametersβ€”same concept, different name). We can just leave the numbers we want to add inΒ r0Β andΒ r1, call the functionΒ sum_x_y, and expect the result to be inΒ r0Β after it finishes.

Did you notice something β€œunderhanded” going on between the caller (main) and the callee (sum_x_y)? There is an implicit contract/agreement as to

  • which registers hold the input arguments, and
  • which registers hold the result

This is calledΒ calling convention, a set of rules that all function calls are expected to adhere to. It is generally CPU architecture and programming language defined. There is more to calling convention, and it will be covered in lecture and in the next lab.

Note that there are some comments before the function about where the arguments are placed. It’s a good idea to document what these registers are expected to hold for readability and clarity’s sake.

Parameterise yourΒ set_bitΒ andΒ clear_bitΒ functions so that they each take three arguments:Β base address,Β offsetΒ andΒ bit index. Modify yourΒ mainΒ function so that turning the LED on and off is as easy as calling yourΒ set_bitΒ orΒ clear_bitΒ functions with the right arguments. Commit & push your newly-functional program to GitLab.

You might have noticed that we haven’t told you to store theΒ lrΒ register onto the stack–that’s cause you’re creating what are called β€œleaf” functions. These leaf functionsΒ don’tΒ call other functions, so don’t need to worry about havingΒ lrΒ overwritten.

Exercise 5: blinky

In this exercise you’ll add a simple loop into your program to blink one of the LEDs on and off. You can write aΒ delayΒ function to do thisβ€”do it now, and call it in-between your on and off instructions. Let the function take in an argument so that you can specify the number of steps to β€œdelay” for.

There are a bunch of ways to do this, but one way is to

  1. subtract 1 from an input register,
  2. ifΒ the value isn’t zeroΒ thenΒ goto step 1,Β elseΒ return back to the caller.

Modify your program so that after the initial setup code, there is a loop which turns the LED on, delays a little while, turns it back off, delays a little again, then branches back to the top of the loop.

Once you’ve done that, you should be able to blink the LEDs on your board to your heart’s content.

For the final exercise, you’ll make LED blinking more interesting by writing an ARM assembly version of the classicΒ FizzBuzzΒ childrens game (and a commonΒ programming interview question). The only difference is that instead of printingΒ "fizz"Β orΒ "buzz"Β to the screen (which you can’t do anyway, since we’re not running on the computer, you’re running on the discoboard) you’ll blink the LEDS on the board. So this new version is calledΒ FizzBlink, I guess.

Modify your program to:

  1. count up fromΒ 0Β toΒ 100Β in increments ofΒ 1
  2. if the number is divisible byΒ 3, blink theΒ redΒ light for some period of time (use your delay function)
  3. if the number is divisible byΒ 5, blink theΒ greenΒ light for some period of time
  4. if the number is divisible by bothΒ 3Β andΒ 5, blinkΒ bothΒ lights

1 file Β· 93.4 KB

  • comp2300-2021-lab-6-ie8la1.zip 93.4 KB