Skip to content

Commit d7b444a

Browse files
committed
stm32l0: add flash support
Flash on the STM32L0 series of chips works a bit different and needs a slightly different implementation compared to other STM32 chips.
1 parent 8ef36ed commit d7b444a

File tree

3 files changed

+169
-1
lines changed

3 files changed

+169
-1
lines changed

src/machine/flash.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
1+
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
22

33
package machine
44

src/machine/machine_stm32_flash.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
package machine
44

5+
// Flash support for STM32 chips, except for STM32L0 which have a different type
6+
// of flash.
7+
58
import (
69
"device/stm32"
710

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//go:build stm32l0
2+
3+
package machine
4+
5+
// The STM32L0 series of MCUs has a different type of flash than other STM32
6+
// series chips. The programming interface is different, and the flash is erased
7+
// to zero bits instead of one bits as on most flash. So this requires a
8+
// different implementation.
9+
10+
import (
11+
"device/stm32"
12+
"runtime/interrupt"
13+
"runtime/volatile"
14+
"unsafe"
15+
)
16+
17+
// compile-time check for ensuring we fulfill BlockDevice interface
18+
var _ BlockDevice = flashBlockDevice{}
19+
20+
var Flash flashBlockDevice
21+
22+
type flashBlockDevice struct {
23+
}
24+
25+
// ReadAt reads the given number of bytes from the block device.
26+
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
27+
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
28+
return 0, errFlashCannotReadPastEOF
29+
}
30+
31+
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
32+
copy(p, data)
33+
34+
return len(p), nil
35+
}
36+
37+
// WriteAt writes the given number of bytes to the block device.
38+
// Only word-sized (32 bits) length data can be programmed.
39+
// If the length of p is not long enough it will be padded with zero bytes.
40+
// This method assumes that the destination is already erased.
41+
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
42+
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
43+
return 0, errFlashCannotWritePastEOF
44+
}
45+
if uintptr(off)%4 != 0 {
46+
// Offset must be aligned on a word boundary.
47+
return 0, errFlashCannotWriteData
48+
}
49+
50+
unlockFlash()
51+
defer lockFlash()
52+
53+
// Write words in this area.
54+
for i := 0; i < len(p); i += 4 {
55+
// Construct the word to write.
56+
word := uint32(p[i])
57+
if i+1 < len(p) {
58+
word |= uint32(p[i+1]) << 8
59+
}
60+
if i+2 < len(p) {
61+
word |= uint32(p[i+2]) << 16
62+
}
63+
if i+3 < len(p) {
64+
word |= uint32(p[i+3]) << 24
65+
}
66+
67+
// Find the pointer address to write.
68+
address := FlashDataStart() + uintptr(off) + uintptr(i)
69+
70+
// Write the word to flash.
71+
(*volatile.Register32)(unsafe.Pointer(address)).Set(word)
72+
73+
// Check for any errors.
74+
if stm32.FLASH.SR.Get()&(stm32.Flash_SR_WRPERR|stm32.Flash_SR_NOTZEROERR|stm32.Flash_SR_SIZERR) != 0 {
75+
return i, errFlashCannotWriteData
76+
}
77+
}
78+
79+
return len(p), nil
80+
}
81+
82+
// Size returns the number of bytes in this block device.
83+
func (f flashBlockDevice) Size() int64 {
84+
return int64(FlashDataEnd() - FlashDataStart())
85+
}
86+
87+
// WriteBlockSize returns the block size in which data can be written to
88+
// memory. It can be used by a client to optimize writes, non-aligned writes
89+
// should always work correctly.
90+
func (f flashBlockDevice) WriteBlockSize() int64 {
91+
return 4
92+
}
93+
94+
func eraseBlockSize() int64 {
95+
return 128
96+
}
97+
98+
// EraseBlockSize returns the smallest erasable area on this particular chip
99+
// in bytes. This is used for the block size in EraseBlocks.
100+
// It must be a power of two, and may be as small as 1. A typical size is 4096.
101+
func (f flashBlockDevice) EraseBlockSize() int64 {
102+
return eraseBlockSize()
103+
}
104+
105+
// EraseBlocks erases the given number of blocks. An implementation may
106+
// transparently coalesce ranges of blocks into larger bundles if the chip
107+
// supports this. The start and len parameters are in block numbers, use
108+
// EraseBlockSize to map addresses to blocks.
109+
// Note that block 0 should map to the address of FlashDataStart().
110+
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
111+
// Flash needs to be unlocked to be able to erase it.
112+
unlockFlash()
113+
defer lockFlash()
114+
115+
// Set the flash programming mode to erase a page.
116+
// Note: lockFlash() will reset these flags to 0 so we don't need to
117+
// explicitly set them to 0.
118+
stm32.FLASH.PECR.Set(stm32.Flash_PECR_ERASE | stm32.Flash_PECR_PROG)
119+
120+
// Erase all pages in this range.
121+
for i := uintptr(start); i < uintptr(start)+uintptr(len); i++ {
122+
// Find the pointer address somewhere in the page to erase.
123+
address := FlashDataStart() + i*uintptr(eraseBlockSize())
124+
125+
// To erase, write any value to that address.
126+
(*volatile.Register32)(unsafe.Pointer(address)).Set(uint32(address))
127+
128+
// Check for any errors.
129+
// The only error (that is not a programming error) that could happen is
130+
// if a row is in a protected sector.
131+
if stm32.FLASH.SR.Get()&(stm32.Flash_SR_WRPERR|stm32.Flash_SR_SIZERR) != 0 {
132+
return errFlashCannotErasePage
133+
}
134+
}
135+
136+
return nil
137+
}
138+
139+
func unlockFlash() {
140+
// Make sure the flash peripheral clock is enabled.
141+
stm32.RCC.AHBENR.SetBits(stm32.RCC_AHBENR_MIFEN)
142+
143+
// Wait for the flash memory not to be busy.
144+
for stm32.FLASH.GetSR_BSY() != 0 {
145+
}
146+
147+
// Disable interrupts while writing, since no memory operations may happen
148+
// while the unlock sequence is ongoing.
149+
mask := interrupt.Disable()
150+
151+
// Remove PELOCK bit.
152+
stm32.FLASH.PEKEYR.Set(0x89ABCDEF)
153+
stm32.FLASH.PEKEYR.Set(0x02030405)
154+
155+
// Remove PRGLOCK bit.
156+
stm32.FLASH.PRGKEYR.Set(0x8C9DAEBF)
157+
stm32.FLASH.PRGKEYR.Set(0x13141516)
158+
159+
interrupt.Restore(mask)
160+
}
161+
162+
func lockFlash() {
163+
// Set PELOCK to 1, which also automatically sets PRGLOCK to 1.
164+
stm32.FLASH.PECR.Set(stm32.Flash_PECR_PELOCK)
165+
}

0 commit comments

Comments
 (0)