---
title: "긴 SPI MRAM 전송이 끝 주소에서 처음으로 돌아오는 함정"
author: "브레이브브라우저쓰자"
published_at: "2026-10-15T08:14:00.246Z"
updated_at: "2026-10-06T00:38:42.822Z"
tags: ["mram address wrap"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/%EA%B8%B4-spi-mram-%EC%A0%84%EC%86%A1%EC%9D%B4-%EB%81%9D-%EC%A3%BC%EC%86%8C%EC%97%90%EC%84%9C-%EC%B2%98%EC%9D%8C%EC%9C%BC%EB%A1%9C-%EB%8F%8C%EC%95%84%EC%98%A4%EB%8A%94-%ED%95%A8%EC%A0%95"
ai_assisted: true
---

# 긴 SPI MRAM 전송이 끝 주소에서 처음으로 돌아오는 함정

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/%EA%B8%B4-spi-mram-%EC%A0%84%EC%86%A1%EC%9D%B4-%EB%81%9D-%EC%A3%BC%EC%86%8C%EC%97%90%EC%84%9C-%EC%B2%98%EC%9D%8C%EC%9C%BC%EB%A1%9C-%EB%8F%8C%EC%95%84%EC%98%A4%EB%8A%94-%ED%95%A8%EC%A0%95)

SPI MRAM의 끝부분에 log를 쓰다가 메모리 맨 앞의 설정 값이 바뀌었다. 주소가 모자라면 장치가 전송을 거절할 것이라는 가정이 틀렸을 수 있다. MRAM Address Wrap은 연속 접근의 주소 counter가 끝에서 처음으로 돌아오는 동작이다.

wrap이 있는 장치에서는 긴 전송 자체가 잘못된 명령일 필요는 없다. 보낸 데이터가 정상적으로 기록되더라도 host가 생각한 영역 밖으로 돌아가 덮어쓸 수 있다. 저장할 수 있는 용량과 wire에 실어 보내는 주소의 폭을 분리해서 읽어야 한다.

## 24bit 주소가 24bit 저장 공간은 아니다

Everspin MR20H40/MR25H40의 Rev.12.6 문서는 4Mb array를 512K×8로 구성한다. READ와 WRITE는 24bit 주소를 받지만 실제로 decode하는 것은 bit 0–18이다. 연속 접근의 주소 counter가 메모리 끝에 도달하면 0000h로 돌아간다고 명시한다. [원문 p.5 및 p.12](https://www.everspin.com/sites/default/files/EST00459_MR2xH40_Datasheet_Rev12.6_08092020.pdf)

byte 주소 개수는 2¹⁹=524,288개, 즉 512KiB다. 유효 범위는 0x00000부터 0x7FFFF까지다. command 형식에 3byte 주소가 있다고 16MiB 영역이 존재하는 것은 아니다.

이 decode 규칙에서는 host가 0x080000을 주소로 보냈더라도 하위 19bit는 0이다. 존재하지 않는 새 영역을 선택하는 대신 처음 주소와 같은 하위 bit를 제공한다. 높은 주소 bit를 활용해 별도 영역을 만드는 설계는 장치의 실제 decode 조건과 맞지 않는다.

## 작은 배열에서 덮어쓰기를 추적한다

계산을 쉽게 하려고 같은 rollover 규칙을 가진 가상의 16byte 배열을 쓰자. 시작 주소는 14이고 다섯 byte A,B,C,D,E를 연속 전송한다. 이때 목적지 주소는 14,15,0,1,2다.

끝의 두 byte만 갱신하려던 host라면 C,D,E가 주소 0–2의 기존 데이터를 덮어쓴다. 명령이 정상적으로 끝났고 readback이 보낸 순서를 재현하더라도, 영역 침범이 없었다는 뜻은 아니다. read 자체도 같은 wrap 규칙을 따르면 잘못된 범위를 같은 순서로 읽어 검증이 통과할 수 있다.

실제 512KiB 제품에서 0x7FFFC부터 8byte를 쓰면 처음 네 byte는 0x7FFFC–0x7FFFF, 나머지 네 byte는 0x00000–0x00003에 들어간다. 배열의 마지막 유효 주소 뒤가 오류 영역으로 남아 있는 것이 아니다.

전송 길이가 배열보다 더 크면 이미 이번 명령에서 쓴 byte를 다시 덮어쓴다. 16byte 배열의 주소 14부터 20byte b0…b19를 쓰는 가상 예에서는 주소 14,15,0,1이 두 번 방문된다. 마지막에는 각각 b16,b17,b18,b19가 남는다. 20byte를 전송했다는 사실이 20개의 서로 다른 저장 칸에 보관했다는 뜻은 아니다.

## driver가 경계를 계산해야 한다

wrap을 원하지 않는 접근이라면 주소 a와 길이 L에 대해 먼저 a가 0 이상 N 미만인지 확인하고, L≤N−a인지 확인하는 방식이 명확하다. N은 byte 용량이다. 단순히 a+L을 계산한 뒤 비교하면 사용하는 정수형의 overflow 때문에 잘못된 범위를 통과시키는 구현도 생길 수 있다.

유효한 시작 주소가 끝에 가깝고 요청이 두 영역으로 나뉘어야 한다면 전송을 끊을 수 있다. 앞의 실제 예는 끝의 4byte를 한 명령으로 쓰고, 다음 4byte가 의도적으로 처음 주소에 속할 때만 새 명령을 시작한다. 하드웨어 wrap이 우연히 해 준 결과보다 host가 정한 영역과 순서를 코드에서 드러낸다.

읽기도 같은 경계를 지켜야 한다. 길이가 큰 read가 처음 영역으로 돌아가면 반복된 데이터가 새로운 뒤쪽 데이터처럼 보일 수 있다. 주소만 확인하고 길이를 확인하지 않는 검증은 절반의 검증이다.

다른 용량이나 다른 인터페이스 제품에는 decode 비트와 rollover 규칙이 다를 수 있다. 특정 제품의 상위 주소 무시를 모든 MRAM에 적용하면 안 된다. driver가 지원하는 모델별 용량과 command 주소 형식을 함께 가지고 있어야 한다.

## 의도한 순환과 우연한 침범을 구별한다

ring buffer에서는 처음으로 돌아오는 동작이 필요하다. 그래도 물리 wrap만으로 log가 완성되지는 않는다. 남겨야 할 record의 경계, 읽기·쓰기 위치, 오래된 데이터의 수명과 전원 중단 뒤 복구 규칙이 필요하다. 한 번의 긴 전송이 아직 소비하지 않은 record를 덮어쓰지 않는지도 따로 판단한다.

특히 설정 영역과 log 영역이 같은 chip에 있을 때 전체 배열의 rollover를 작은 log 영역의 rollover로 착각하면 위험하다. chip은 소프트웨어가 나눈 partition을 알지 못한다. log의 끝에서 다시 log의 시작으로 돌아오는 동작은 host가 해당 범위 안에서 구현해야 한다.

MRAM Address Wrap을 안전하게 사용하는 기준은 끝 주소의 존재보다 넘어갔을 때의 목적지를 아는 것이다. 실제 decode 폭과 byte 용량을 읽고 전송 길이를 함께 검증하면, 정상적인 연속 쓰기가 왜 다른 영역을 덮어썼는지 설명할 수 있다. 의도된 순환만 저장 정책에 포함시키는 것이 그 동작을 다루는 방법이다.

