Skip to content

Commit 6062eae

Browse files
committed
Merge branch 'spi-memory-api'
2 parents 465b4da + 39e0dc9 commit 6062eae

File tree

5 files changed

+358
-6
lines changed

5 files changed

+358
-6
lines changed

external/asf4-drivers/hpl/spi/spi_lite.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
*/
3434

3535
#include "spi_lite.h"
36+
#include <stdint.h>
3637
#include <utils_assert.h>
3738

3839
/**
@@ -119,7 +120,7 @@ uint32_t SPI_MEM_exchange_data(uint32_t data)
119120
return hri_sercomspi_read_DATA_reg(SERCOM4);
120121
}
121122

122-
void SPI_MEM_exchange_block(void *block, uint8_t size)
123+
void SPI_MEM_exchange_block(void *block, size_t size)
123124
{
124125

125126
uint8_t *b = (uint8_t *)block;
@@ -133,7 +134,7 @@ void SPI_MEM_exchange_block(void *block, uint8_t size)
133134
}
134135
}
135136

136-
void SPI_MEM_write_block(void *block, uint8_t size)
137+
void SPI_MEM_write_block(void *block, size_t size)
137138
{
138139

139140
uint8_t *b = (uint8_t *)block;
@@ -145,7 +146,7 @@ void SPI_MEM_write_block(void *block, uint8_t size)
145146
}
146147
}
147148

148-
void SPI_MEM_read_block(void *block, uint8_t size)
149+
void SPI_MEM_read_block(void *block, size_t size)
149150
{
150151

151152
uint8_t *b = (uint8_t *)block;

external/asf4-drivers/hpl/spi/spi_lite.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,17 @@ uint32_t SPI_MEM_exchange_data(uint32_t data);
8989
/**
9090
* \brief Exchange block in SPI module
9191
*/
92-
void SPI_MEM_exchange_block(void *block, uint8_t size);
92+
void SPI_MEM_exchange_block(void *block, size_t size);
9393

9494
/**
9595
* \brief Write block in SPI module
9696
*/
97-
void SPI_MEM_write_block(void *block, uint8_t size);
97+
void SPI_MEM_write_block(void *block, size_t size);
9898

9999
/**
100100
* \brief Read block in SPI module
101101
*/
102-
void SPI_MEM_read_block(void *block, uint8_t size);
102+
void SPI_MEM_read_block(void *block, size_t size);
103103

104104
// Calculate baud register value from requested baudrate value
105105
#ifndef SERCOM3_BAUD_RATE

src/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ set(DBB-FIRMWARE-SOURCES
2929
${CMAKE_SOURCE_DIR}/src/memory/memory_shared.c
3030
${CMAKE_SOURCE_DIR}/src/memory/mpu.c
3131
${CMAKE_SOURCE_DIR}/src/memory/nvmctrl.c
32+
${CMAKE_SOURCE_DIR}/src/memory/spi_mem.c
3233
${CMAKE_SOURCE_DIR}/src/memory/smarteeprom.c
3334
${CMAKE_SOURCE_DIR}/src/salt.c
3435
${CMAKE_SOURCE_DIR}/src/i2c_ecc.c
@@ -108,6 +109,7 @@ set(DBB-BOOTLOADER-SOURCES
108109
${CMAKE_SOURCE_DIR}/src/memory/memory_shared.c
109110
${CMAKE_SOURCE_DIR}/src/memory/mpu.c
110111
${CMAKE_SOURCE_DIR}/src/memory/nvmctrl.c
112+
${CMAKE_SOURCE_DIR}/src/memory/spi_mem.c
111113
${CMAKE_SOURCE_DIR}/src/queue.c
112114
${CMAKE_SOURCE_DIR}/src/usb/usb_processing.c
113115
${CMAKE_SOURCE_DIR}/src/ui/ugui/ugui.c

src/memory/spi_mem.c

+258
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "spi_mem.h"
16+
#include "util.h"
17+
#include <stdbool.h>
18+
#include <stdint.h>
19+
#include <stdlib.h>
20+
#ifndef TESTING
21+
#include "bitbox02_pins.h"
22+
#include <hal_delay.h>
23+
#include <spi_lite.h>
24+
#endif
25+
26+
#define SECTOR_MASK 0xFFFFF000
27+
#define MEMORY_LIMIT (SPI_MEM_MEMORY_SIZE - 1)
28+
#define SR_WIP 0x01
29+
#define CMD_READ 0x03
30+
#define CMD_WREN 0x06
31+
#define CMD_SE 0x20
32+
#define CMD_PP 0x02
33+
#define CMD_RDSR 0x05
34+
#define CMD_CE 0x60
35+
36+
static void _spi_mem_cs_low(void)
37+
{
38+
#ifndef TESTING
39+
gpio_set_pin_level(PIN_MEM_CS, 0);
40+
#endif
41+
}
42+
43+
static void _spi_mem_cs_high(void)
44+
{
45+
#ifndef TESTING
46+
gpio_set_pin_level(PIN_MEM_CS, 1);
47+
#endif
48+
}
49+
50+
static uint8_t _spi_mem_read_sr(void)
51+
{
52+
uint8_t buffer[2] = {0};
53+
buffer[0] = CMD_RDSR;
54+
_spi_mem_cs_low();
55+
SPI_MEM_exchange_block(buffer, 2);
56+
_spi_mem_cs_high();
57+
return buffer[1];
58+
}
59+
60+
static void _spi_mem_read(uint32_t address, size_t size, uint8_t* buffer)
61+
{
62+
buffer[0] = CMD_READ;
63+
buffer[1] = (address >> 16) & 0xFF;
64+
buffer[2] = (address >> 8) & 0xFF;
65+
buffer[3] = address & 0xFF;
66+
memset(&buffer[4], 0x00, size);
67+
68+
_spi_mem_cs_low();
69+
SPI_MEM_exchange_block(buffer, size + 4);
70+
_spi_mem_cs_high();
71+
}
72+
73+
static void _spi_mem_wait(void)
74+
{
75+
uint8_t status;
76+
do {
77+
status = _spi_mem_read_sr();
78+
} while (status & SR_WIP);
79+
}
80+
81+
void spi_mem_full_erase(void)
82+
{
83+
uint8_t buffer[2];
84+
85+
// --- Enable Write ---
86+
buffer[0] = CMD_WREN;
87+
_spi_mem_cs_low();
88+
SPI_MEM_exchange_block(buffer, 1);
89+
_spi_mem_cs_high();
90+
91+
// --- Chip Erase ---
92+
buffer[0] = CMD_CE;
93+
_spi_mem_cs_low();
94+
SPI_MEM_exchange_block(buffer, 1);
95+
_spi_mem_cs_high();
96+
97+
_spi_mem_wait();
98+
}
99+
100+
bool spi_mem_sector_erase(uint32_t sector_addr)
101+
{
102+
if (sector_addr & ~SECTOR_MASK || (sector_addr + SPI_MEM_SECTOR_SIZE - 1) > MEMORY_LIMIT) {
103+
util_log("Invalid sector address %p", (void*)(uintptr_t)sector_addr);
104+
return false;
105+
}
106+
107+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
108+
// --- Enable Write ---
109+
buffer[0] = CMD_WREN;
110+
_spi_mem_cs_low();
111+
SPI_MEM_exchange_block(buffer, 1);
112+
_spi_mem_cs_high();
113+
114+
// --- Sector Erase (write 4 bytes) ---
115+
buffer[0] = CMD_SE;
116+
buffer[1] = (sector_addr >> 16) & 0xFF;
117+
buffer[2] = (sector_addr >> 8) & 0xFF;
118+
buffer[3] = sector_addr & 0xFF;
119+
120+
_spi_mem_cs_low();
121+
SPI_MEM_exchange_block(buffer, 4);
122+
_spi_mem_cs_high();
123+
124+
// --- Wait for write to end ---
125+
_spi_mem_wait();
126+
127+
return true;
128+
}
129+
130+
bool spi_mem_page_read(uint32_t page_addr, uint8_t* data_out)
131+
{
132+
if (page_addr % SPI_MEM_PAGE_SIZE != 0) {
133+
util_log("Invalid page read address %p", (void*)(uintptr_t)page_addr);
134+
return false;
135+
}
136+
137+
uint8_t tmp_buf[SPI_MEM_PAGE_SIZE + 4];
138+
_spi_mem_read(page_addr, SPI_MEM_PAGE_SIZE, tmp_buf);
139+
memcpy(data_out, &tmp_buf[4], SPI_MEM_PAGE_SIZE);
140+
return true;
141+
}
142+
143+
uint8_t* spi_mem_read(uint32_t address, size_t size)
144+
{
145+
if (address + size - 1 > MEMORY_LIMIT || size < 1) {
146+
util_log("Invalid read address %p or size %i", (void*)(uintptr_t)address, (int)size);
147+
return NULL;
148+
}
149+
150+
uint8_t* buffer = malloc(size + 4);
151+
if (!buffer) {
152+
util_log("Memory allocation failed");
153+
return NULL;
154+
}
155+
156+
_spi_mem_read(address, size, buffer);
157+
158+
// shift the read data at the beginning of the buffer, overriding the command and the address.
159+
for (size_t i = 0; i < size; i++) {
160+
buffer[i] = buffer[i + 4];
161+
}
162+
return buffer;
163+
}
164+
165+
static bool _spi_mem_page_write(uint32_t page_addr, const uint8_t* input)
166+
{
167+
if (page_addr % SPI_MEM_PAGE_SIZE != 0) {
168+
util_log("Invalid page write address %p", (void*)(uintptr_t)page_addr);
169+
return false;
170+
}
171+
172+
uint8_t buffer[SPI_MEM_PAGE_SIZE + 4];
173+
// --- Enable Write ---
174+
buffer[0] = CMD_WREN;
175+
_spi_mem_cs_low();
176+
SPI_MEM_exchange_block(buffer, 1);
177+
_spi_mem_cs_high();
178+
179+
// --- Page Program (write 4 bytes) ---
180+
buffer[0] = CMD_PP;
181+
buffer[1] = (page_addr >> 16) & 0xFF;
182+
buffer[2] = (page_addr >> 8) & 0xFF;
183+
buffer[3] = page_addr & 0xFF;
184+
memcpy(&buffer[4], input, SPI_MEM_PAGE_SIZE);
185+
186+
_spi_mem_cs_low();
187+
SPI_MEM_exchange_block(buffer, 4 + SPI_MEM_PAGE_SIZE);
188+
_spi_mem_cs_high();
189+
190+
// --- Wait for write to end ---
191+
_spi_mem_wait();
192+
193+
return true;
194+
}
195+
196+
bool spi_mem_write(uint32_t address, const uint8_t* input, size_t size)
197+
{
198+
if (address + size - 1 > MEMORY_LIMIT || size < 1) {
199+
util_log("Invalid write address %p or size %i", (void*)(uintptr_t)address, (int)size);
200+
return false;
201+
}
202+
203+
uint32_t initial_sector_addr = address & SECTOR_MASK;
204+
uint32_t final_sector_addr = ((address + size - 1) & SECTOR_MASK) + SPI_MEM_SECTOR_SIZE;
205+
uint16_t sectors = (final_sector_addr - initial_sector_addr) / SPI_MEM_SECTOR_SIZE;
206+
207+
// read all the affected sectors data
208+
uint8_t* buffer = spi_mem_read(initial_sector_addr, (size_t)(sectors * SPI_MEM_SECTOR_SIZE));
209+
if (!buffer) {
210+
return false;
211+
}
212+
213+
// update data in the buffer
214+
memcpy(&buffer[address - initial_sector_addr], input, size);
215+
216+
// erase sectors and write data
217+
for (uint32_t i = 0; i < sectors; i++) {
218+
uint32_t sector_addr = initial_sector_addr + (i * SPI_MEM_SECTOR_SIZE);
219+
if (!spi_mem_sector_erase(sector_addr)) {
220+
free(buffer);
221+
return false;
222+
}
223+
for (uint32_t p = 0; p < (SPI_MEM_SECTOR_SIZE / SPI_MEM_PAGE_SIZE); p++) {
224+
uint32_t page_addr = sector_addr + p * SPI_MEM_PAGE_SIZE;
225+
if (!_spi_mem_page_write(
226+
page_addr, &buffer[(i * SPI_MEM_SECTOR_SIZE) + (p * SPI_MEM_PAGE_SIZE)])) {
227+
free(buffer);
228+
return false;
229+
}
230+
}
231+
}
232+
free(buffer);
233+
return true;
234+
}
235+
236+
int32_t spi_mem_smart_erase(void)
237+
{
238+
uint32_t erased_sectors = 0;
239+
uint8_t buffer[SPI_MEM_SECTOR_SIZE + 4];
240+
for (uint32_t i = 0; i < (SPI_MEM_MEMORY_SIZE / SPI_MEM_SECTOR_SIZE); i++) {
241+
_spi_mem_read(i * SPI_MEM_SECTOR_SIZE, SPI_MEM_SECTOR_SIZE, buffer);
242+
for (size_t j = 0; j < SPI_MEM_SECTOR_SIZE; j++) {
243+
if (buffer[j + 4] != 0xFF) {
244+
util_log(
245+
"Sector at address 0x%06X not erased. Erasing...",
246+
(unsigned int)(i * SPI_MEM_SECTOR_SIZE));
247+
if (!spi_mem_sector_erase(i * SPI_MEM_SECTOR_SIZE)) {
248+
util_log("Error erasing sector.");
249+
return -1;
250+
}
251+
erased_sectors++;
252+
break;
253+
}
254+
}
255+
}
256+
257+
return erased_sectors;
258+
}

0 commit comments

Comments
 (0)