diff --git a/Cargo.toml b/Cargo.toml index 0029c8b9e10e46f747ed8d913deffd491e8fa30e..272d24440c89f0822e18f0c891575355470eaa0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/examples/rtic_bare1.rs b/examples/rtic_bare1.rs index 11248f4e1eaa427af7aff5f812ad18ea94a4037c..15cd93fd74dd141f06779be7d7b2a87888440bbb 100644 --- a/examples/rtic_bare1.rs +++ b/examples/rtic_bare1.rs @@ -20,14 +20,15 @@ const APP: () = { fn init(_cx: init::Context) { let mut x = core::u32::MAX - 1; loop { - // cortex_m::asm::bkpt(); - x += 1; - // cortex_m::asm::bkpt(); + //cortex_m::asm::bkpt(); + //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) // diff --git a/examples/rtic_bare2.rs b/examples/rtic_bare2.rs index 04266d7cbbfa598a29b99e95f7310f0c80c0967c..7143d10549794cfd02d0d57bdf15b7de6cb6586f 100644 --- a/examples/rtic_bare2.rs +++ b/examples/rtic_bare2.rs @@ -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) diff --git a/examples/rtic_bare3.rs b/examples/rtic_bare3.rs index 9173175c051d4b3e4615e0db20d2b08265d59763..d5f8934e1fd9cd081127280d4fafe20ef760e691 100644 --- a/examples/rtic_bare3.rs +++ b/examples/rtic_bare3.rs @@ -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) // diff --git a/examples/rtic_bare4.rs b/examples/rtic_bare4.rs index 1d53d13715b0afe372d3ab430e5fe1c301fb740a..f6e3e299ff67aa20f7e7d602a638af518a4f5a84 100644 --- a/examples/rtic_bare4.rs +++ b/examples/rtic_bare4.rs @@ -36,7 +36,7 @@ use address::*; #[inline(always)] fn read_u32(addr: u32) -> u32 { unsafe { core::ptr::read_volatile(addr as *const _) } - // core::ptr::read_volatile(addr as *const _) + //core::ptr::read_volatile(addr as *const _) } #[inline(always)] @@ -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). diff --git a/examples/rtic_bare5.rs b/examples/rtic_bare5.rs index 5686e1b7a24c32db2f45dd120b87c33605af10ee..b62fad244e5fba92abfe27bc7dcfc761bc677998 100644 --- a/examples/rtic_bare5.rs +++ b/examples/rtic_bare5.rs @@ -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 @@ -185,7 +197,7 @@ const APP: () = { // alternatively to set the bit high we can // read the value, or with PA5 (bit 5) and write back - // gpioa.ODR.write(gpioa.ODR.read() | (1 << 5)); + //gpioa.ODR.write(gpioa.ODR.read() | (1 << 5)); wait(10_000); @@ -194,7 +206,7 @@ const APP: () = { // alternatively to clear the bit we can // read the value, mask out PA5 (bit 5) and write back - // gpioa.ODR.write(gpioa.ODR.read() & !(1 << 5)); + //gpioa.ODR.write(gpioa.ODR.read() & !(1 << 5)); wait(10_000); } } @@ -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)