Skip to content
Snippets Groups Projects
Commit 2509ec53 authored by Carl Österberg's avatar Carl Österberg
Browse files

bare1-5

parent 9eb5c17a
Branches
No related tags found
No related merge requests found
......@@ -17,7 +17,7 @@ usb-device = "0.2.7"
panic-halt = "0.2.0"
# Uncomment for the itm panic examples.
#panic-itm = "0.4.2"
panic-itm = "0.4.2"
# Uncomment for the rtt-timing examples.
panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
......
......@@ -21,13 +21,14 @@ const APP: () = {
let mut x = core::u32::MAX - 1;
loop {
//cortex_m::asm::bkpt();
x += 1;
//x = x.wrapping_add(1);
//cortex_m::asm::bkpt();
x += 1;
// prevent optimization by read-volatile (unsafe)
unsafe {
core::ptr::read_volatile(&x);
}
// unsafe {
// core::ptr::read_volatile(&x);
// }
}
}
};
......@@ -45,11 +46,13 @@ const APP: () = {
//
// Paste the error message:
//
// ** your answer here **
// panicked at 'attempt to add with overflow', examples/rtic_bare1.rs:24:13
//
// Explain in your own words why the code panic:ed.
//
// ** your answer here **
// Before adding to the register, it holds the value 2^32 - 1 = 4294967295 = 0xffffffff which is the max value of a u32.
// When 1 is added to this register the register will become 0x00000001 plus a carry bit, this is known as overflow. Beacuse the addition is not
// defined in rust to be wrapping instead a panic is invoced.
//
// Commit your answer (bare1_1)
//
......@@ -64,12 +67,14 @@ const APP: () = {
// > backtrace
//
// Paste the backtrace:
// #0 rtic_bare1::init (_cx=...) at /home/carlosterberg/Skrivbord/e7020e_2021/examples/rtic_bare1.rs:29
// #1 0x08000f08 in rtic_bare1::APP::main () at /home/carlosterberg/Skrivbord/e7020e_2021/examples/rtic_bare1.rs:15
// {"token":59,"outOfBandRecord":[],"resultRecords":{"resultClass":"done","results":[]}}
//
// ** your answer here
//
// Explain in your own words the chain of calls.
//
// ** your answer here
// First RTIC sets up with init, then our own function is scheduled and then run in main
//
// Commit your answer (bare1_2)
//
......@@ -82,20 +87,23 @@ const APP: () = {
//
// What is the value of `x`?
//
// ** your answer here **
// 4,294,967,294
//
// Explain in your own words where this value comes from.
//
// ** your answer here **
// the value 2^32 - 1 = 4294967295 = 0xffffffff which is the max value of a u32. In 32 bit architecture the registers are 32-bits.
// Then we take this value minus one.
//
// Now continue the program, since you are in a loop
// the program will halt again at line 24.
//
// What is the value of `x`?
//
// 4,294,967,295
//
// Explain in your own words why `x` now has this value.
//
// ** your answer here **
// We increment by one each iteration.
//
// Now continue again.
//
......@@ -109,7 +117,8 @@ const APP: () = {
//
// Explain in your own words why a panic makes sense at this point.
//
// ** your answer here **
// To rust a wrapping addition is wrong and should produce a error, compared to languages like C where wrapping is ok,
// which means that in C this addition wouldnt produce an error.
//
// Commit your answer (bare1_3)
//
......@@ -126,13 +135,14 @@ const APP: () = {
//
// Explain in your own words what this assembly line does.
//
// ** your answer here **
// ldr means loads from memory to register, so we load from sp (stackpointer) with offset 0, which contains our x. Then we save this register to r0
// for further manipulation.
//
// In Cortex Registers (left) you can see the content of `r0`
//
// What value do you observe?
//
// ** your answer here **
// -2
//
// You can also get the register info from GDB directly.
//
......@@ -150,7 +160,7 @@ const APP: () = {
//
// Explain in your own words what is happening here.
//
// ** your answer here **
// One is added to register r0
//
// We move to the next assembly instruction:
//
......@@ -159,7 +169,7 @@ const APP: () = {
//
// What is the reported value for `r0`
//
// ** your answer here **
// -1
//
// So far so good.
//
......@@ -190,7 +200,8 @@ const APP: () = {
//
// What does BCS do?
//
// ** your answer here **
// BCS stands for "Branch if Carry is Set", which means that it sets the programm counter to a specified instruction if
// the carry bit is set.
//
// Now let's see what happens.
//
......@@ -204,7 +215,8 @@ const APP: () = {
//
// Explain in your own words where we are heading.
//
// ** your answer here **
// From branch and link instruction we see that we are on our way into the rust panic handler.
//
//
// To validate that your answer, let's let the program continue
//
......@@ -220,7 +232,8 @@ const APP: () = {
// Hint 3, the code is generated by the Rust compiler to produce the error message.
// there is no "magic" here, just a compiler generating code...
//
// ** your answer here **
// We load values into r0, r1 and r2 which probably has to do with the rust error message,
// then we branch to the rust panic implementation.
//
// Commit your answer (bare1_4)
//
......@@ -273,7 +286,7 @@ const APP: () = {
//
// Do you see any way this code may end up in a panic?
//
// ** your answer here **
// No this code cant panic. It will continue to add 1 and just wrap.
//
// So clearly, the "semantics" (meaning) of the program has changed.
// This is on purpose, Rust adopts "unchecked" (wrapping) additions (and subtractions)
......@@ -288,16 +301,28 @@ const APP: () = {
//
// Paste the generated assembly:
//
// ** your answer here **
// Dump of assembler code for function rtic_bare1::init:
// 0x08000240 <+0>: push {r7, lr}
// 0x08000242 <+2>: mov r7, sp
// 0x08000244 <+4>: sub sp, #8
// 0x08000246 <+6>: mvn.w r0, #1
// 0x0800024a <+10>: str r0, [sp, #4]
// => 0x0800024c <+12>: bkpt 0x0000
// 0x0800024e <+14>: adds r0, #1
// 0x08000250 <+16>: str r0, [sp, #4]
// 0x08000252 <+18>: bkpt 0x0000
// 0x08000254 <+20>: ldr r0, [sp, #4]
// 0x08000256 <+22>: b.n 0x800024c <rtic_bare1::init+12>
// End of assembler dump.
//
// Can this code generate a panic?
//
// ** your answer here **
// No there isnt any branch on carry to the rust panic implementation.
//
// Is there now any reference to the panic handler?
// If not, why is that the case?
//
// ** your answer here **
// The addition will always work therefor no error to catch and no need to conditionally branch to panic handler.
//
// commit your answers (bare1_5)
//
......@@ -327,12 +352,18 @@ const APP: () = {
//
// Dump the generated assembly.
//
// ** your answer here **
// Dump of assembler code for function rtic_bare1::init:
//
// 0x08000240 <+0>: push {r7, lr}
// 0x08000242 <+2>: mov r7, sp
// => 0x08000244 <+4>: bkpt 0x0000
// 0x08000246 <+6>: bkpt 0x0000
// 0x08000248 <+8>: b.n 0x8000244 <rtic_bare1::init+4>
//
// Where is the local variable stored?
// What happened, and why is Rust + LLVM allowed to optimize out your code?
//
// ** your answer here **
// The variable is gone, the loop is now just a two breakpoints. Beacuse we dont use x anywhere else, its optimized out.
//
// Commit your answers (bare1_6)
//
......
......@@ -39,7 +39,7 @@ const APP: () = {
hprintln!("End {:?}", end).ok();
hprintln!("Diff {:?}", end.wrapping_sub(start)).ok();
// wait(100);
wait(100);
}
};
......@@ -71,16 +71,20 @@ fn wait(i: u32) {
// What is the output in the Adapter Output console?
// (Notice, it will take a while we loop one million times at only 16 MHz.)
//
// ** your answer here **
// Start 2123685351
// End 2290685434
// Diff 167000083
//
// Rebuild and run in (Cortex Release).
//
// ** your answer here **
// Start 2165548795
// End 2169548811
// Diff 4000016
//
// Compute the ratio between debug/release optimized code
// (the speedup).
//
// ** your answer here **
// Release is 167000083/4000016=41,749853751 times as fast
//
// commit your answers (bare2_1)
//
......@@ -105,7 +109,14 @@ fn wait(i: u32) {
//
// Dump generated assembly for the "wait" function.
//
// ** your answer here **
// 0x080004a0 <+0>: push {r7, lr}
// 0x080004a2 <+2>: mov r7, sp
// 0x080004a4 <+4>: movw r0, #16960 ; 0x4240
// 0x080004a8 <+8>: movt r0, #15
// 0x080004ac <+12>: nop
// 0x080004ae <+14>: subs r0, #1
// 0x080004b0 <+16>: bne.n 0x80004ac <rtic_bare2::wait+12>
// => 0x080004b2 <+18>: pop {r7, pc}
//
// Under the ARM calling convention, r0.. is used as arguments.
// However in this case, we se that r0 is set by the assembly instructions,
......@@ -115,7 +126,9 @@ fn wait(i: u32) {
//
// Answer in your own words, how they assign r0 to 1000000.
//
// ** your answer here **
// movw r0, #16960 sets r0 [15:0] to 16960
// movt r0, #15 sets r0 [31:16] to 15
// which together makes r0 =1000000
//
// Commit your answers (bare2_2)
//
......@@ -125,10 +138,17 @@ fn wait(i: u32) {
//
// Dump the generated assembly for the "wait" function.
//
// ** your answer here **
// 0x080004a0 <+0>: push {r7, lr}
// 0x080004a2 <+2>: mov r7, sp
// 0x080004a4 <+4>: nop
// 0x080004a6 <+6>: subs r0, #1
// 0x080004a8 <+8>: bne.n 0x80004a4 <rtic_bare2::wait+4>
// => 0x080004aa <+10>: pop {r7, pc}
//
// Answer in your own words, why you believe the generated code differs?
//
// ** your answer here **
// Beacuse there are multiple calls to wait this time around the r0 register is used as argument and is set before entering the wait function.
// Before, this was optimized away beacuse only one call was made to wait, which meant that no arguments was really needed and r0 could be set
// in the function instead.
//
// Commit your answers (bare2_3)
......@@ -28,7 +28,9 @@ const APP: () = {
// notice all printing outside of the section to measure!
hprintln!("Start {:?}", start).ok();
hprintln!("End {:?}", end).ok();
// hprintln!("Diff {:?}", (end - start) ).ok();
//hprintln!("Diff {:?}", (end - start).as_cycles()).ok();
//hprintln!("Diff {:?}", end.duration_since(start).as_cycles()).ok();
hprintln!("Diff {:?}", (start.elapsed() - end.elapsed()).as_cycles()).ok();
}
};
......@@ -62,7 +64,8 @@ fn wait(i: u32) {
//
// What is the output in the Adapter Output console?
//
// ** your answer here **
// Start Instant(2702015064)
// End Instant(2706015081)
//
// As you see line 31 is commented out (we never print the difference).
//
......@@ -76,7 +79,9 @@ fn wait(i: u32) {
//
// What is now the output in the Adapter Output console?
//
// ** your answer here **
// Start Instant(3042949340)
// End Instant(3046949356)
// Diff 4000016
//
// Commit your answers (bare3_1)
//
......@@ -86,7 +91,9 @@ fn wait(i: u32) {
//
// What is now the output in the Adapter Output console?
//
// ** your answer here **
// Start Instant(2178201621)
// End Instant(2182201637)
// Diff 4000016
//
// Commit your answers (bare3_2)
//
......@@ -95,7 +102,9 @@ fn wait(i: u32) {
//
// What is now the output in the Adapter Output console?
//
// ** your answer here **
// Start Instant(1175751045)
// End Instant(1179751061)
// Diff 4000009
//
// Commit your answers (bare3_3)
//
......
......@@ -83,7 +83,7 @@ const APP: () = {
//
// 1. Did you enjoy the blinking?
//
// ** your answer here **
// yes thank you very much my life is better now.
//
// Now lookup the data-sheets, and read each section referred,
// 6.3.11, 8.4.1, 8.4.7
......@@ -100,12 +100,14 @@ const APP: () = {
//
// What was the error message and explain why.
//
// ** your answer here **
// error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
// A volatile read to register clashes with rusts memory safety, therefor we must tell the compiler
// that we know what we are doing and just go with it.
//
// Digging a bit deeper, why do you think `read_volatile` is declared `unsafe`.
// (https://doc.rust-lang.org/core/ptr/fn.read_volatile.html, for some food for thought )
//
// ** your answer here **
// Races are undefined behaviour in rust.
//
// Commit your answers (bare4_2)
//
......@@ -118,12 +120,12 @@ const APP: () = {
//
// Why is it important that ordering of volatile operations are ensured by the compiler?
//
// ** your answer here **
// If a read and write operation change places the read operation will have a completly different outcome.
//
// Give an example in the above code, where reordering might make things go horribly wrong
// (hint, accessing a peripheral not being powered...)
//
// ** your answer here **
// If we try to access GPIOA_MODER before its powered we wont be able to write to it.
//
// Without the non-reordering property of `write_volatile/read_volatile` could that happen in theory
// (argue from the point of data dependencies).
......
......@@ -11,6 +11,7 @@
extern crate cortex_m;
extern crate panic_semihosting;
use cortex_m_semihosting::hprintln;
// C like API...
mod stm32f40x {
......@@ -25,6 +26,7 @@ mod stm32f40x {
pub const GPIOA_BASE: u32 = AHB1PERIPH_BASE + 0x0000;
}
use address::*;
use cortex_m_semihosting::hprintln;
pub struct VolatileCell<T> {
pub value: cell::UnsafeCell<T>,
......@@ -58,7 +60,13 @@ mod stm32f40x {
impl VolatileCell<u32> {
#[inline(always)]
pub fn modify(&self, offset: u8, width: u8, value: u32) {
// your code here
let mut mask = core::u32::MAX;
mask = mask >> (32-width);
mask = mask << offset;
let altered = mask & (value << offset);
//hprintln!("Altered {:b}", !altered).ok();
//hprintln!("Old {:b}", (self.read()&!mask)).ok();
self.write((self.read()&!mask) | altered);
}
}
......@@ -151,12 +159,16 @@ fn test_modify() {
// ..0111000
// ---------
// 000101000
//hprintln!("Current {:b}", t.read()).ok();
//hprintln!("Correct {:b}", 0b101 << 3).ok();
assert!(t.read() == 0b101 << 3);
t.modify(4, 3, 0b10001);
// 000101000
// 111
// 001
// 000011000
//hprintln!("Current {:b}", t.read()).ok();
//hprintln!("Correct {:b}", 0b011 << 3).ok();
assert!(t.read() == 0b011 << 3);
//
// add more tests here if you like
......@@ -177,7 +189,7 @@ const APP: () = {
let r = gpioa.MODER.read() & !(0b11 << (5 * 2)); // read and mask
gpioa.MODER.write(r | 0b01 << (5 * 2)); // set output mode
// test_modify();
test_modify();
loop {
// set PA5 high
......@@ -241,6 +253,6 @@ const APP: () = {
// What if we could automatically generate that from Vendors specifications (SVD files)?
// Wouldn't that be great?
//
// ** your answer here **
// It sure would be :)
//
// Commit your answers (bare5_2)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment