Skip to content

Commit

Permalink
Merge #2936
Browse files Browse the repository at this point in the history
2936: Stm32f4xx: Dma2 & USART1 + Stm32f429idiscovery BSP r=hudson-ayers a=LeonMatthesKDAB

### Pull Request Overview

This PR originates from my need to get Tock OS running on a [Stm32f429i discovery](https://www.st.com/en/evaluation-tools/32f429idiscovery.html) board.
The board layout was copied & adapted from the Nucleo_f429zi code.

The ST-Link connector that this board uses to connect to the PC is only connected to USART1, which must be configured with the DMA2 controller.
This PR therefore adds the ability for Tock OS to enable the DMA2 & USART1  on the Stm32f4xx platforms.

Even though the Stm32f429iDISC BSP should probably not be added to Tock OS, adding the DMA2 and USART1 is surely useful.

### Testing Strategy

This pull request was tested by connecting to the the STM32F429IDiscovery board via it's ST-Link interface.
UART output this way is working for both in- and output. The Tock OS console is accessible that way.

I'd be happy to receive suggestions on how to automate testing for this 🤔 

### TODO

- [x] Merge more code of Dma1 & Dma2 into the shared `dma` module (e.g. DmaClock)
- [x] Fix wildcard `use super::*` in `dma1.rs` and `dma2.rs`
- [x] Adapt other Stm32f4xx chips&boards to the new API

### Help Wanted

As this introduces a new dma peripheral, as well as a new USART stream, it breaks the internal Kernel API in quite a few places.
Is this approach viable, or should complete API backward-compatibility be the target? This doesn't change any User-Space APIs though.

I've kept Dma1&2 as separate types for now, so that Dma2 peripherals can not accidentally be passed into the Dma1 or vice-versa.
That does however mean, that the code for Usart, as well as all BSPs based on the Stm32f4xx boards need to be adapted to deal with this. If someone knows a way to implement this backwards-compatible, that would be great!

Currently, I've added a generic DmaPeripheral as well as Stream enum types that simply wrap either a Dma1Peripheral/Dma2Peripheral or Stream respectively, so that i.e. Usart can deal with either.
Feedback on this approach is much appreciated. I can also see something based on traits and dyn references to be viable, but I'm unsure if this is suitable in the embedded context of Tock OS.

### Documentation Updated

- As this change is entirely `chip` and `board` specific code, no additional changes in `doc` should be required.

### Formatting

- [x] Ran `make prepush`.


Co-authored-by: Leon Matthes <leon@matthes.biz>
  • Loading branch information
bors[bot] and LeonMatthes committed Mar 26, 2022
2 parents bea1932 + c95cc8d commit 55fbff7
Show file tree
Hide file tree
Showing 29 changed files with 1,775 additions and 602 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -34,6 +34,7 @@ members = [
"boards/redboard_redv",
"boards/stm32f3discovery",
"boards/stm32f412gdiscovery",
"boards/stm32f429idiscovery",
"boards/teensy40",
"boards/nano33ble",
"boards/swervolf",
Expand Down
18 changes: 10 additions & 8 deletions boards/nucleo_f429zi/src/main.rs
Expand Up @@ -127,11 +127,11 @@ impl

/// Helper function called during bring-up that configures DMA.
unsafe fn setup_dma(
dma: &stm32f429zi::dma1::Dma1,
dma_streams: &'static [stm32f429zi::dma1::Stream; 8],
usart3: &'static stm32f429zi::usart::Usart,
dma: &stm32f429zi::dma::Dma1,
dma_streams: &'static [stm32f429zi::dma::Stream<stm32f429zi::dma::Dma1>; 8],
usart3: &'static stm32f429zi::usart::Usart<stm32f429zi::dma::Dma1>,
) {
use stm32f429zi::dma1::Dma1Peripheral;
use stm32f429zi::dma::Dma1Peripheral;
use stm32f429zi::usart;

dma.enable_clock();
Expand Down Expand Up @@ -259,7 +259,7 @@ unsafe fn setup_peripherals(tim2: &stm32f429zi::tim2::Tim2) {
unsafe fn get_peripherals() -> (
&'static mut Stm32f429ziDefaultPeripherals<'static>,
&'static stm32f429zi::syscfg::Syscfg<'static>,
&'static stm32f429zi::dma1::Dma1<'static>,
&'static stm32f429zi::dma::Dma1<'static>,
) {
// We use the default HSI 16Mhz clock
let rcc = static_init!(stm32f429zi::rcc::Rcc, stm32f429zi::rcc::Rcc::new());
Expand All @@ -271,10 +271,12 @@ unsafe fn get_peripherals() -> (
stm32f429zi::exti::Exti,
stm32f429zi::exti::Exti::new(syscfg)
);
let dma1 = static_init!(stm32f429zi::dma1::Dma1, stm32f429zi::dma1::Dma1::new(rcc));
let dma1 = static_init!(stm32f429zi::dma::Dma1, stm32f429zi::dma::Dma1::new(rcc));
let dma2 = static_init!(stm32f429zi::dma::Dma2, stm32f429zi::dma::Dma2::new(rcc));

let peripherals = static_init!(
Stm32f429ziDefaultPeripherals,
Stm32f429ziDefaultPeripherals::new(rcc, exti, dma1)
Stm32f429ziDefaultPeripherals::new(rcc, exti, dma1, dma2)
);
(peripherals, syscfg, dma1)
}
Expand All @@ -296,7 +298,7 @@ pub unsafe fn main() {

setup_dma(
dma1,
&base_peripherals.dma_streams,
&base_peripherals.dma1_streams,
&base_peripherals.usart3,
);

Expand Down
18 changes: 10 additions & 8 deletions boards/nucleo_f446re/src/main.rs
Expand Up @@ -125,11 +125,11 @@ impl

/// Helper function called during bring-up that configures DMA.
unsafe fn setup_dma(
dma: &stm32f446re::dma1::Dma1,
dma_streams: &'static [stm32f446re::dma1::Stream; 8],
usart2: &'static stm32f446re::usart::Usart,
dma: &stm32f446re::dma::Dma1,
dma_streams: &'static [stm32f446re::dma::Stream<stm32f446re::dma::Dma1>; 8],
usart2: &'static stm32f446re::usart::Usart<stm32f446re::dma::Dma1>,
) {
use stm32f446re::dma1::Dma1Peripheral;
use stm32f446re::dma::Dma1Peripheral;
use stm32f446re::usart;

dma.enable_clock();
Expand Down Expand Up @@ -211,7 +211,7 @@ unsafe fn setup_peripherals(tim2: &stm32f446re::tim2::Tim2) {
unsafe fn get_peripherals() -> (
&'static mut Stm32f446reDefaultPeripherals<'static>,
&'static stm32f446re::syscfg::Syscfg<'static>,
&'static stm32f446re::dma1::Dma1<'static>,
&'static stm32f446re::dma::Dma1<'static>,
) {
// We use the default HSI 16Mhz clock
let rcc = static_init!(stm32f446re::rcc::Rcc, stm32f446re::rcc::Rcc::new());
Expand All @@ -223,10 +223,12 @@ unsafe fn get_peripherals() -> (
stm32f446re::exti::Exti,
stm32f446re::exti::Exti::new(syscfg)
);
let dma1 = static_init!(stm32f446re::dma1::Dma1, stm32f446re::dma1::Dma1::new(rcc));
let dma1 = static_init!(stm32f446re::dma::Dma1, stm32f446re::dma::Dma1::new(rcc));
let dma2 = static_init!(stm32f446re::dma::Dma2, stm32f446re::dma::Dma2::new(rcc));

let peripherals = static_init!(
Stm32f446reDefaultPeripherals,
Stm32f446reDefaultPeripherals::new(rcc, exti, dma1)
Stm32f446reDefaultPeripherals::new(rcc, exti, dma1, dma2)
);
(peripherals, syscfg, dma1)
}
Expand All @@ -248,7 +250,7 @@ pub unsafe fn main() {

setup_dma(
dma1,
&base_peripherals.dma_streams,
&base_peripherals.dma1_streams,
&base_peripherals.usart2,
);

Expand Down
18 changes: 10 additions & 8 deletions boards/stm32f412gdiscovery/src/main.rs
Expand Up @@ -132,11 +132,11 @@ impl

/// Helper function called during bring-up that configures DMA.
unsafe fn setup_dma(
dma: &stm32f412g::dma1::Dma1,
dma_streams: &'static [stm32f412g::dma1::Stream; 8],
usart2: &'static stm32f412g::usart::Usart,
dma: &stm32f412g::dma::Dma1,
dma_streams: &'static [stm32f412g::dma::Stream<stm32f412g::dma::Dma1>; 8],
usart2: &'static stm32f412g::usart::Usart<stm32f412g::dma::Dma1>,
) {
use stm32f412g::dma1::Dma1Peripheral;
use stm32f412g::dma::Dma1Peripheral;
use stm32f412g::usart;

dma.enable_clock();
Expand Down Expand Up @@ -372,7 +372,7 @@ unsafe fn setup_peripherals(
unsafe fn get_peripherals() -> (
&'static mut Stm32f412gDefaultPeripherals<'static>,
&'static stm32f412g::syscfg::Syscfg<'static>,
&'static stm32f412g::dma1::Dma1<'static>,
&'static stm32f412g::dma::Dma1<'static>,
) {
let rcc = static_init!(stm32f412g::rcc::Rcc, stm32f412g::rcc::Rcc::new());
let syscfg = static_init!(
Expand All @@ -381,11 +381,13 @@ unsafe fn get_peripherals() -> (
);

let exti = static_init!(stm32f412g::exti::Exti, stm32f412g::exti::Exti::new(syscfg));
let dma1 = static_init!(stm32f412g::dma1::Dma1, stm32f412g::dma1::Dma1::new(rcc));

let dma1 = static_init!(stm32f412g::dma::Dma1, stm32f412g::dma::Dma1::new(rcc));
let dma2 = static_init!(stm32f412g::dma::Dma2, stm32f412g::dma::Dma2::new(rcc));

let peripherals = static_init!(
Stm32f412gDefaultPeripherals,
Stm32f412gDefaultPeripherals::new(rcc, exti, dma1)
Stm32f412gDefaultPeripherals::new(rcc, exti, dma1, dma2)
);
(peripherals, syscfg, dma1)
}
Expand All @@ -411,7 +413,7 @@ pub unsafe fn main() {

setup_dma(
dma1,
&base_peripherals.dma_streams,
&base_peripherals.dma1_streams,
&base_peripherals.usart2,
);

Expand Down
13 changes: 13 additions & 0 deletions boards/stm32f429idiscovery/Cargo.toml
@@ -0,0 +1,13 @@
[package]
name = "stm32f429idiscovery"
version = "0.1.0"
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
build = "build.rs"
edition = "2018"

[dependencies]
components = { path = "../components" }
cortexm4 = { path = "../../arch/cortex-m4" }
capsules = { path = "../../capsules" }
kernel = { path = "../../kernel" }
stm32f429zi = { path = "../../chips/stm32f429zi" }
27 changes: 27 additions & 0 deletions boards/stm32f429idiscovery/Makefile
@@ -0,0 +1,27 @@
# Makefile for building the tock kernel for the STM32F429i Discovery board
#
TARGET=thumbv7em-none-eabi
PLATFORM=stm32f429idiscovery

include ../Makefile.common

OPENOCD=openocd
OPENOCD_OPTIONS=-f openocd.cfg

.PHONY: flash-debug
flash-debug: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).elf
$(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $<; verify_image $<; reset; shutdown"

.PHONY: flash
flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).elf
$(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $<; verify_image $<; reset; shutdown"

APP=../../../libtock-rs/target/thumbv7em-none-eabi/tab/stm32f429idiscovery/hello_world/cortex-m4.tbf
#APP=../../../libtock-rs/target/thumbv7em-none-eabi/tab/stm32f429idiscovery/blink/cortex-m4.tbf
KERNEL=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM).elf
KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM)-app.elf

.PHONY: program
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).elf
arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP)
$(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown"
59 changes: 59 additions & 0 deletions boards/stm32f429idiscovery/README.md
@@ -0,0 +1,59 @@
STM32F429I Discovery development board with STM32F429ZI MCU
======================================================

Note: This board layout is based on the nucleo_f429zi board layout.

For more details [visit the STM32F429I Discovery website](https://www.st.com/en/evaluation-tools/32f429idiscovery.html).

## Flashing the kernel

The kernel can be programmed using OpenOCD. `cd` into `boards/std32f429idiscovery`
directory and run:

```bash
$ make flash

(or)

$ make flash-debug
```

> **Note:** Unlike other Tock platforms, the default kernel image for this
> board will clear flashed apps when the kernel is loaded. This is to support
> the non-tockloader based app flash procedure below. To preserve loaded apps,
> comment out the `APP_HACK` variable in `src/main.rs`.
## Flashing app

Apps are built out-of-tree. Once an app is built, you can use
`arm-none-eabi-objcopy` with `--update-section` to create an ELF image with the
apps included.

```bash
$ arm-none-eabi-objcopy \
--update-section .apps=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf \
target/thumbv7em-none-eabi/debug/stm32f429idiscovery.elf \
target/thumbv7em-none-eabi/debug/stm32f429idiscovery-app.elf
```

For example, you can update `Makefile` as follows.

```
APP=../../../libtock-c/examples/c_hello/build/cortex-m4/cortex-m4.tbf
KERNEL=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM).elf
KERNEL_WITH_APP=$(TOCK_ROOT_DIRECTORY)/target/$(TARGET)/debug/$(PLATFORM)-app.elf
.PHONY: program
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).elf
arm-none-eabi-objcopy --update-section .apps=$(APP) $(KERNEL) $(KERNEL_WITH_APP)
$(OPENOCD) $(OPENOCD_OPTIONS) -c "init; reset halt; flash write_image erase $(KERNEL_WITH_APP); verify_image $(KERNEL_WITH_APP); reset; shutdown"
```

After setting `APP`, `KERNEL`, `KERNEL_WITH_APP`, and `program` target
dependency, you can do

```bash
$ make program
```

to flash the image.
5 changes: 5 additions & 0 deletions boards/stm32f429idiscovery/build.rs
@@ -0,0 +1,5 @@
fn main() {
println!("cargo:rerun-if-changed=layout.ld");
println!("cargo:rerun-if-changed=chip_layout.ld");
println!("cargo:rerun-if-changed=../kernel_layout.ld");
}
15 changes: 15 additions & 0 deletions boards/stm32f429idiscovery/chip_layout.ld
@@ -0,0 +1,15 @@
/* Memory layout for the STM32F446RE
* rom = 2MB (LENGTH = 0x02000000)
* kernel = 256KB
* user = 256KB
* ram = 192KB */

MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 0x00040000
prog (rx) : ORIGIN = 0x08040000, LENGTH = 0x00040000
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000
}

MPU_MIN_ALIGN = 8K;
PAGE_SIZE = 2K;
2 changes: 2 additions & 0 deletions boards/stm32f429idiscovery/layout.ld
@@ -0,0 +1,2 @@
INCLUDE ./chip_layout.ld
INCLUDE ../kernel_layout.ld
11 changes: 11 additions & 0 deletions boards/stm32f429idiscovery/openocd.cfg
@@ -0,0 +1,11 @@
#interface
interface hla
hla_layout stlink
hla_device_desc "ST-LINK/V2-1"
hla_vid_pid 0x0483 0x374b

# The chip has 128KB sram, but we will still use 64KB
set WORKAREASIZE 0x10000

source [find target/stm32f4x.cfg]

90 changes: 90 additions & 0 deletions boards/stm32f429idiscovery/src/io.rs
@@ -0,0 +1,90 @@
use core::fmt::Write;
use core::panic::PanicInfo;

use cortexm4;

use kernel::debug;
use kernel::debug::IoWrite;
use kernel::hil::led;
use kernel::hil::uart;
use kernel::hil::uart::Configure;

use stm32f429zi;
use stm32f429zi::gpio::PinId;

use crate::CHIP;
use crate::PROCESSES;
use crate::PROCESS_PRINTER;

/// Writer is used by kernel::debug to panic message to the serial port.
pub struct Writer {
initialized: bool,
}

/// Global static for debug writer
pub static mut WRITER: Writer = Writer { initialized: false };

impl Writer {
/// Indicate that USART has already been initialized. Trying to double
/// initialize USART1 causes stm32f429zi to go into in in-deterministic state.
pub fn set_initialized(&mut self) {
self.initialized = true;
}
}

impl Write for Writer {
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
self.write(s.as_bytes());
Ok(())
}
}

impl IoWrite for Writer {
fn write(&mut self, buf: &[u8]) {
let rcc = stm32f429zi::rcc::Rcc::new();
let uart = stm32f429zi::usart::Usart::new_usart1(&rcc);

if !self.initialized {
self.initialized = true;

let _ = uart.configure(uart::Parameters {
baud_rate: 115200,
stop_bits: uart::StopBits::One,
parity: uart::Parity::None,
hw_flow_control: false,
width: uart::Width::Eight,
});
}

for &c in buf {
uart.send_byte(c);
}
}
}

/// Panic handler.
#[no_mangle]
#[panic_handler]
pub unsafe extern "C" fn panic_fmt(info: &PanicInfo) -> ! {
// User LD4 is connected to PG14
// Have to reinitialize several peripherals because otherwise can't access them here.
let rcc = stm32f429zi::rcc::Rcc::new();
let syscfg = stm32f429zi::syscfg::Syscfg::new(&rcc);
let exti = stm32f429zi::exti::Exti::new(&syscfg);
let pin = stm32f429zi::gpio::Pin::new(PinId::PG14, &exti);
let gpio_ports = stm32f429zi::gpio::GpioPorts::new(&rcc, &exti);
pin.set_ports_ref(&gpio_ports);
let led = &mut led::LedHigh::new(&pin);

let writer = &mut WRITER;

debug::panic(
&mut [led],
writer,
info,
&cortexm4::support::nop,
&PROCESSES,
&CHIP,
&PROCESS_PRINTER,
)
}

0 comments on commit 55fbff7

Please sign in to comment.