Skip to content

Commit 5e4f842

Browse files
committed
stm32h7-startup: move pre_init into assembly.
Our pre-init routine contains things that need to happen before anything touches RAM. This is not reliably possible from a Rust function, since the compiler may elect to set up a stack frame (and, in our case, definitely does so today). This commit moves it into straight assembly language, ensuring that we have control over the use of RAM. As a pleasant side effect, this also makes it really hard to accidentally put Rust code before it. This is not sufficient, by itself, to eliminate use of RAM early in boot, since the ancient version of cortex-m-rt that we're using has a bug here. But I'll tackle that in a follow-on commit.
1 parent 2dc17d2 commit 5e4f842

File tree

1 file changed

+72
-52
lines changed

1 file changed

+72
-52
lines changed

drv/stm32h7-startup/src/lib.rs

Lines changed: 72 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,64 +4,84 @@
44

55
#![no_std]
66

7-
use cortex_m_rt::pre_init;
8-
97
#[cfg(feature = "h743")]
108
use stm32h7::stm32h743 as device;
119

1210
#[cfg(feature = "h753")]
1311
use stm32h7::stm32h753 as device;
1412

15-
#[cfg(any(feature = "h743", feature = "h753"))]
16-
#[pre_init]
17-
unsafe fn system_pre_init() {
18-
// Configure the power supply to latch the LDO on and prevent further
19-
// reconfiguration.
20-
//
21-
// Normally we would use Peripherals::take() to safely get a reference to
22-
// the PWR block, but that function expects RAM to be initialized and
23-
// writable. At this point, RAM is neither -- because the chip requires us
24-
// to get the power supply configuration right _before it guarantees that
25-
// RAM will work._
26-
//
27-
// Another case of the cortex_m/stm32 crates being designed with simpler
28-
// systems in mind.
29-
30-
// Synthesize a pointer using a const fn (which won't hit RAM) and then
31-
// convert it to a reference. We can have a reference to PWR because it's
32-
// hardware, and is thus not uninitialized.
33-
let pwr = &*device::PWR::ptr();
34-
// Poke CR3 to enable the LDO and prevent further writes.
35-
pwr.cr3.modify(|_, w| w.ldoen().set_bit());
36-
37-
// Busy-wait until the ACTVOSRDY bit says that we've stabilized at VOS3.
38-
while !pwr.csr1.read().actvosrdy().bit() {
39-
// spin
40-
}
41-
42-
// Turn on the internal RAMs.
43-
let rcc = &*device::RCC::ptr();
44-
rcc.ahb2enr.modify(|_, w| {
45-
w.sram1en()
46-
.set_bit()
47-
.sram2en()
48-
.set_bit()
49-
.sram3en()
50-
.set_bit()
51-
});
52-
53-
// Okay, yay, we can use some RAMs now.
54-
55-
#[cfg(any(feature = "h743", feature = "h753"))]
56-
{
57-
// Workaround for erratum 2.2.9 "Reading from AXI SRAM may lead to data
58-
// read corruption" - limits AXI SRAM read concurrency.
59-
let axi = &*device::AXI::ptr();
60-
axi.targ7_fn_mod
61-
.modify(|_, w| w.read_iss_override().set_bit());
62-
}
63-
64-
// We'll do the rest in system_init.
13+
// System pre-init hook for establishing system properties required by Rust.
14+
//
15+
// This routine must run before anything touches RAM! The cortex-m-rt crate's
16+
// Reset handler ensures this. As a result, we have to write this in raw
17+
// assembly code, to avoid trying to push/pop a stack frame.
18+
//
19+
// Be very careful about reordering or removing things from this function.
20+
core::arch::global_asm! {
21+
".global __pre_init",
22+
".type __pre_init_,%function",
23+
".thumb_func",
24+
".cfi_startproc",
25+
"__pre_init:",
26+
27+
// PWR.CR3 has a write-once feature on the LDO enable bit. The processor
28+
// would like the power configuration to be stable before it guarantees that
29+
// writes to RAM will succeed (reference manual 6.4.1 "System supply
30+
// startup"). We're actually perfectly happy with the reset supply
31+
// configuration, which is VOS3 on the LDO. So, we'll write PWR.CR3 just to
32+
// lock it:
33+
" movw r0, :lower16:{PWR_addr}",
34+
" movt r0, :upper16:{PWR_addr}",
35+
" ldr r1, [r0, #{PWR_CR3_offset}]",
36+
" str r1, [r0, #{PWR_CR3_offset}]",
37+
38+
// Technically we're supposed to ensure that we're stable at VOS3 before
39+
// continuing; this should already be ensured before our code was allowed to
40+
// run, but for safety's sake:
41+
"1: ldr r1, [r0, #{PWR_CSR1_offset}]",
42+
" tst r1, #(1 << {PWR_CSR1_ACTVOSRDY_bit})",
43+
" beq 1b",
44+
45+
// Turn on all of the smaller non-TCM non-AXI SRAMs, in case the program
46+
// puts data there.
47+
" movw r0, :lower16:{RCC_addr}",
48+
" movt r0, :upper16:{RCC_addr}",
49+
" ldr r1, [r0, #{RCC_AHB2ENR_offset}]",
50+
" orrs r1, #((1 << {RCC_AHB2ENR_SRAM1EN_bit}) \
51+
| (1 << {RCC_AHB2ENR_SRAM2EN_bit}) \
52+
| (1 << {RCC_AHB2ENR_SRAM3EN_bit}))",
53+
54+
// Apply workaround for ST erratum 2.2.9 "Reading from AXI SRAM may lead to
55+
// data read corruption" - limits AXI SRAM read concurrency.
56+
" movw r0, :lower16:{AXI_TARG7_FN_MOD_addr}",
57+
" movt r0, :upper16:{AXI_TARG7_FN_MOD_addr}",
58+
" ldr r1, [r0]",
59+
" orrs r1, #(1 << {AXI_TARG7_FN_MOD_READ_ISS_OVERRIDE_bit})",
60+
" str r1, [r0]",
61+
62+
// Aaaaand we're done.
63+
" bx lr",
64+
".cfi_endproc",
65+
".size __pre_init, . - __pre_init",
66+
67+
PWR_addr = const 0x5802_4800, //device::PWR::ptr(),
68+
PWR_CSR1_offset = const 0x4, // reference manual 6.8.2
69+
PWR_CSR1_ACTVOSRDY_bit = const 13,
70+
PWR_CR3_offset = const 0xC, // reference manual 6.8.4
71+
72+
RCC_addr = const 0x5802_4400, //device::RCC::ptr(),
73+
RCC_AHB2ENR_offset = const 0x0DC, // reference manual 8.7.42
74+
RCC_AHB2ENR_SRAM1EN_bit = const 29, // reference manual 8.7.42
75+
RCC_AHB2ENR_SRAM2EN_bit = const 30, // reference manual 8.7.42
76+
RCC_AHB2ENR_SRAM3EN_bit = const 31, // reference manual 8.7.42
77+
78+
79+
// The offset from the AXI block to this register is too large to do the
80+
// same base/displacement thing as the other peripherals above, so this
81+
// constant is the result of adding the base address from the reference
82+
// manual (0x5100_0000) to the offset from table 6.
83+
AXI_TARG7_FN_MOD_addr = const 0x5100_8108,
84+
AXI_TARG7_FN_MOD_READ_ISS_OVERRIDE_bit = const 0, // same
6585
}
6686

6787
pub struct ClockConfig {

0 commit comments

Comments
 (0)