aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-07-09 02:13:43 +0200
committerDario Nieuwenhuis <[email protected]>2022-07-09 02:14:30 +0200
commitccf57cfab63d9562d617a667ef6dfa2c9edd572f (patch)
treee325a0d1151022e14563311c708366d709253375
parent5cc5961c94568db768c241e8e8bf91a692c1803e (diff)
rp: add GPIO HIL test.
-rwxr-xr-xci.sh1
-rw-r--r--tests/rp/.cargo/config.toml20
-rw-r--r--tests/rp/Cargo.toml48
-rw-r--r--tests/rp/build.rs16
-rw-r--r--tests/rp/link_ram.x255
-rw-r--r--tests/rp/src/bin/gpio.rs192
6 files changed, 532 insertions, 0 deletions
diff --git a/ci.sh b/ci.sh
index 6d7c9b046..315b60ef7 100755
--- a/ci.sh
+++ b/ci.sh
@@ -104,6 +104,7 @@ cargo batch \
104 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ 104 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \
105 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ 105 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \
106 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ 106 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \
107 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
107 108
108 109
109function run_elf { 110function run_elf {
diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml
new file mode 100644
index 000000000..0330025e4
--- /dev/null
+++ b/tests/rp/.cargo/config.toml
@@ -0,0 +1,20 @@
1[unstable]
2build-std = ["core"]
3build-std-features = ["panic_immediate_abort"]
4
5[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf"
7runner = "teleprobe local run --chip RP2040 --elf"
8
9rustflags = [
10 # Code-size optimizations.
11 "-Z", "trap-unreachable=no",
12 "-C", "inline-threshold=5",
13 "-C", "no-vectorize-loops",
14]
15
16[build]
17target = "thumbv6m-none-eabi"
18
19[env]
20DEFMT_LOG = "trace"
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
new file mode 100644
index 000000000..b3067fffd
--- /dev/null
+++ b/tests/rp/Cargo.toml
@@ -0,0 +1,48 @@
1[package]
2edition = "2021"
3name = "embassy-rp-tests"
4version = "0.1.0"
5
6[dependencies]
7embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
8embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] }
9
10defmt = "0.3.0"
11defmt-rtt = "0.3.0"
12
13cortex-m = "0.7.3"
14cortex-m-rt = "0.7.0"
15embedded-hal = "0.2.6"
16embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
17embedded-hal-async = { version = "0.1.0-alpha.1" }
18panic-probe = { version = "0.3.0", features = ["print-defmt"] }
19
20[profile.dev]
21debug = 2
22debug-assertions = true
23opt-level = 's'
24overflow-checks = true
25
26[profile.release]
27codegen-units = 1
28debug = 2
29debug-assertions = false
30incremental = false
31lto = "fat"
32opt-level = 's'
33overflow-checks = false
34
35# do not optimize proc-macro crates = faster builds from scratch
36[profile.dev.build-override]
37codegen-units = 8
38debug = false
39debug-assertions = false
40opt-level = 0
41overflow-checks = false
42
43[profile.release.build-override]
44codegen-units = 8
45debug = false
46debug-assertions = false
47opt-level = 0
48overflow-checks = false
diff --git a/tests/rp/build.rs b/tests/rp/build.rs
new file mode 100644
index 000000000..6f4872249
--- /dev/null
+++ b/tests/rp/build.rs
@@ -0,0 +1,16 @@
1use std::error::Error;
2use std::path::PathBuf;
3use std::{env, fs};
4
5fn main() -> Result<(), Box<dyn Error>> {
6 let out = PathBuf::from(env::var("OUT_DIR").unwrap());
7 fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap();
8 println!("cargo:rustc-link-search={}", out.display());
9 println!("cargo:rerun-if-changed=link_ram.x");
10
11 println!("cargo:rustc-link-arg-bins=--nmagic");
12 println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
13 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
14
15 Ok(())
16}
diff --git a/tests/rp/link_ram.x b/tests/rp/link_ram.x
new file mode 100644
index 000000000..86a11e875
--- /dev/null
+++ b/tests/rp/link_ram.x
@@ -0,0 +1,255 @@
1/* ##### EMBASSY NOTE
2 Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in
3 Adjusted to put everything in RAM
4*/
5
6/* # Developer notes
7
8- Symbols that start with a double underscore (__) are considered "private"
9
10- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
11 overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
12 static mut __sbss }`).
13
14- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
15 symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
16 needed" by any of the preceding objects (linker arguments)
17
18- `PROVIDE` is used to provide default values that can be overridden by a user linker script
19
20- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
21 the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization
22 routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see
23 "Address (..) is out of bounds" in the disassembly produced by `objdump`.
24*/
25
26/* Provides information about the memory layout of the device */
27MEMORY {
28 RAM : ORIGIN = 0x20000000, LENGTH = 256K
29}
30
31/* # Entry point = reset vector */
32EXTERN(__RESET_VECTOR);
33EXTERN(Reset);
34ENTRY(Reset);
35
36/* # Exception vectors */
37/* This is effectively weak aliasing at the linker level */
38/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.
39 the `exception!` macro) */
40EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */
41
42EXTERN(DefaultHandler);
43
44PROVIDE(NonMaskableInt = DefaultHandler);
45EXTERN(HardFaultTrampoline);
46PROVIDE(MemoryManagement = DefaultHandler);
47PROVIDE(BusFault = DefaultHandler);
48PROVIDE(UsageFault = DefaultHandler);
49PROVIDE(SecureFault = DefaultHandler);
50PROVIDE(SVCall = DefaultHandler);
51PROVIDE(DebugMonitor = DefaultHandler);
52PROVIDE(PendSV = DefaultHandler);
53PROVIDE(SysTick = DefaultHandler);
54
55PROVIDE(DefaultHandler = DefaultHandler_);
56PROVIDE(HardFault = HardFault_);
57
58/* # Interrupt vectors */
59EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */
60
61/* # Pre-initialization function */
62/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,
63 then the function this points to will be called before the RAM is initialized. */
64PROVIDE(__pre_init = DefaultPreInit);
65
66/* # Sections */
67SECTIONS
68{
69 PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
70
71 /* ## Sections in RAM */
72 /* ### Vector table */
73 .vector_table ORIGIN(RAM) :
74 {
75 /* Initial Stack Pointer (SP) value */
76 LONG(_stack_start);
77
78 /* Reset vector */
79 KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */
80 __reset_vector = .;
81
82 /* Exceptions */
83 KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */
84 __eexceptions = .;
85
86 /* Device specific interrupts */
87 KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */
88 } > RAM
89
90 PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));
91
92 /* ### .text */
93 .text _stext :
94 {
95 __stext = .;
96 *(.Reset);
97
98 *(.text .text.*);
99
100 /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`,
101 so must be placed close to it. */
102 *(.HardFaultTrampoline);
103 *(.HardFault.*);
104
105 . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */
106 __etext = .;
107 } > RAM
108
109 /* ### .rodata */
110 .rodata : ALIGN(4)
111 {
112 . = ALIGN(4);
113 __srodata = .;
114 *(.rodata .rodata.*);
115
116 /* 4-byte align the end (VMA) of this section.
117 This is required by LLD to ensure the LMA of the following .data
118 section will have the correct alignment. */
119 . = ALIGN(4);
120 __erodata = .;
121 } > RAM
122
123 /* ## Sections in RAM */
124 /* ### .data */
125 .data : ALIGN(4)
126 {
127 . = ALIGN(4);
128 __sdata = .;
129 __edata = .;
130 *(.data .data.*);
131 . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
132 } > RAM
133 /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
134 * use the .data loading mechanism by pushing __edata. Note: do not change
135 * output region or load region in those user sections! */
136 . = ALIGN(4);
137
138 /* LMA of .data */
139 __sidata = LOADADDR(.data);
140
141 /* ### .gnu.sgstubs
142 This section contains the TrustZone-M veneers put there by the Arm GNU linker. */
143 /* Security Attribution Unit blocks must be 32 bytes aligned. */
144 /* Note that this pads the RAM usage to 32 byte alignment. */
145 .gnu.sgstubs : ALIGN(32)
146 {
147 . = ALIGN(32);
148 __veneer_base = .;
149 *(.gnu.sgstubs*)
150 . = ALIGN(32);
151 __veneer_limit = .;
152 } > RAM
153
154 /* ### .bss */
155 .bss (NOLOAD) : ALIGN(4)
156 {
157 . = ALIGN(4);
158 __sbss = .;
159 *(.bss .bss.*);
160 *(COMMON); /* Uninitialized C statics */
161 . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
162 } > RAM
163 /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
164 * use the .bss zeroing mechanism by pushing __ebss. Note: do not change
165 * output region or load region in those user sections! */
166 . = ALIGN(4);
167 __ebss = .;
168
169 /* ### .uninit */
170 .uninit (NOLOAD) : ALIGN(4)
171 {
172 . = ALIGN(4);
173 __suninit = .;
174 *(.uninit .uninit.*);
175 . = ALIGN(4);
176 __euninit = .;
177 } > RAM
178
179 /* Place the heap right after `.uninit` in RAM */
180 PROVIDE(__sheap = __euninit);
181
182 /* ## .got */
183 /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in
184 the input files and raise an error if relocatable code is found */
185 .got (NOLOAD) :
186 {
187 KEEP(*(.got .got.*));
188 }
189
190 /* ## Discarded sections */
191 /DISCARD/ :
192 {
193 /* Unused exception related info that only wastes space */
194 *(.ARM.exidx);
195 *(.ARM.exidx.*);
196 *(.ARM.extab.*);
197 }
198}
199
200/* Do not exceed this mark in the error messages below | */
201/* # Alignment checks */
202ASSERT(ORIGIN(RAM) % 4 == 0, "
203ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned");
204
205ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, "
206BUG(cortex-m-rt): .data is not 4-byte aligned");
207
208ASSERT(__sidata % 4 == 0, "
209BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned");
210
211ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, "
212BUG(cortex-m-rt): .bss is not 4-byte aligned");
213
214ASSERT(__sheap % 4 == 0, "
215BUG(cortex-m-rt): start of .heap is not 4-byte aligned");
216
217/* # Position checks */
218
219/* ## .vector_table */
220ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, "
221BUG(cortex-m-rt): the reset vector is missing");
222
223ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, "
224BUG(cortex-m-rt): the exception vectors are missing");
225
226ASSERT(SIZEOF(.vector_table) > 0x40, "
227ERROR(cortex-m-rt): The interrupt vectors are missing.
228Possible solutions, from most likely to less likely:
229- Link to a svd2rust generated device crate
230- Check that you actually use the device/hal/bsp crate in your code
231- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency
232may be enabling it)
233- Supply the interrupt handlers yourself. Check the documentation for details.");
234
235/* ## .text */
236ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, "
237ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section
238Set _stext to an address greater than the end of .vector_table (See output of `nm`)");
239
240ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), "
241ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory.
242Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'");
243
244/* # Other checks */
245ASSERT(SIZEOF(.got) == 0, "
246ERROR(cortex-m-rt): .got section detected in the input object files
247Dynamic relocations are not supported. If you are linking to C code compiled using
248the 'cc' crate then modify your build script to compile the C code _without_
249the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
250/* Do not exceed this mark in the error messages above | */
251
252
253/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */
254/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */
255INCLUDE device.x \ No newline at end of file
diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs
new file mode 100644
index 000000000..0be9d9f24
--- /dev/null
+++ b/tests/rp/src/bin/gpio.rs
@@ -0,0 +1,192 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{assert, *};
6use embassy::executor::Spawner;
7use embassy_rp::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull};
8use embassy_rp::Peripherals;
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy::main]
12async fn main(_spawner: Spawner, p: Peripherals) {
13 info!("Hello World!");
14
15 let (mut a, mut b) = (p.PIN_0, p.PIN_1);
16
17 // Test initial output
18 {
19 let b = Input::new(&mut b, Pull::None);
20
21 {
22 let _a = Output::new(&mut a, Level::Low);
23 delay();
24 assert!(b.is_low());
25 }
26 {
27 let _a = Output::new(&mut a, Level::High);
28 delay();
29 assert!(b.is_high());
30 }
31 }
32
33 // Test input no pull
34 {
35 let b = Input::new(&mut b, Pull::None);
36 // no pull, the status is undefined
37
38 let mut a = Output::new(&mut a, Level::Low);
39 delay();
40 assert!(b.is_low());
41 a.set_high();
42 delay();
43 assert!(b.is_high());
44 }
45
46 // Test input pulldown
47 {
48 let b = Input::new(&mut b, Pull::Down);
49 delay();
50 assert!(b.is_low());
51
52 let mut a = Output::new(&mut a, Level::Low);
53 delay();
54 assert!(b.is_low());
55 a.set_high();
56 delay();
57 assert!(b.is_high());
58 }
59
60 // Test input pullup
61 {
62 let b = Input::new(&mut b, Pull::Up);
63 delay();
64 assert!(b.is_high());
65
66 let mut a = Output::new(&mut a, Level::Low);
67 delay();
68 assert!(b.is_low());
69 a.set_high();
70 delay();
71 assert!(b.is_high());
72 }
73
74 // OUTPUT OPEN DRAIN
75 {
76 let mut b = OutputOpenDrain::new(&mut b, Level::High);
77 let mut a = Flex::new(&mut a);
78 a.set_as_input();
79
80 // When an OutputOpenDrain is high, it doesn't drive the pin.
81 a.set_pull(Pull::Up);
82 delay();
83 assert!(a.is_high());
84 a.set_pull(Pull::Down);
85 delay();
86 assert!(a.is_low());
87
88 b.set_low();
89
90 // When an OutputOpenDrain is low, it drives the pin low.
91 a.set_pull(Pull::Up);
92 delay();
93 assert!(a.is_low());
94 a.set_pull(Pull::Down);
95 delay();
96 assert!(a.is_low());
97
98 b.set_high();
99
100 a.set_pull(Pull::Up);
101 delay();
102 assert!(a.is_high());
103 a.set_pull(Pull::Down);
104 delay();
105 assert!(a.is_low());
106 }
107
108 // FLEX
109 // Test initial output
110 {
111 //Flex pin configured as input
112 let mut b = Flex::new(&mut b);
113 b.set_as_input();
114
115 {
116 //Flex pin configured as output
117 let mut a = Flex::new(&mut a); //Flex pin configured as output
118 a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state
119 a.set_as_output();
120 delay();
121 assert!(b.is_low());
122 }
123 {
124 //Flex pin configured as output
125 let mut a = Flex::new(&mut a);
126 a.set_high();
127 a.set_as_output();
128
129 delay();
130 assert!(b.is_high());
131 }
132 }
133
134 // Test input no pull
135 {
136 let mut b = Flex::new(&mut b);
137 b.set_as_input(); // no pull by default.
138
139 let mut a = Flex::new(&mut a);
140 a.set_low();
141 a.set_as_output();
142
143 delay();
144 assert!(b.is_low());
145 a.set_high();
146 delay();
147 assert!(b.is_high());
148 }
149
150 // Test input pulldown
151 {
152 let mut b = Flex::new(&mut b);
153 b.set_as_input();
154 b.set_pull(Pull::Down);
155 delay();
156 assert!(b.is_low());
157
158 let mut a = Flex::new(&mut a);
159 a.set_low();
160 a.set_as_output();
161 delay();
162 assert!(b.is_low());
163 a.set_high();
164 delay();
165 assert!(b.is_high());
166 }
167
168 // Test input pullup
169 {
170 let mut b = Flex::new(&mut b);
171 b.set_as_input();
172 b.set_pull(Pull::Up);
173 delay();
174 assert!(b.is_high());
175
176 let mut a = Flex::new(&mut a);
177 a.set_high();
178 a.set_as_output();
179 delay();
180 assert!(b.is_high());
181 a.set_low();
182 delay();
183 assert!(b.is_low());
184 }
185
186 info!("Test OK");
187 cortex_m::asm::bkpt();
188}
189
190fn delay() {
191 cortex_m::asm::delay(10000);
192}