[kernel]플래쉬 메모리 컨트롤

제목: 하드웨어 강좌 (5)

글쓴이: 푸지
글쓴날: 2002년 04월 19일 오전 11:28
URL : http://kelp.or.kr/korweblog/?story=02/04/19/3325938

플래시 메모리 쉽게 끝내기 (2)

*플래시메모리 읽고 쓰기


오랜만에 강좌를 올립니다. 먹고 살려니 어쩔수가 없군요..
이번강좌는 플래시를 직접 제어하겠습니다. 물론 말로만..


2.2.0 스트라타 플래시 읽기
넘쉽다. 기냥주소 주고 읽는다. 메모리하고 같다.

2.2.1 스트라타,부트블럭 플래시 ID 읽기
모든 플래시의 제어에 있어 ID 읽기가 기본이다. (읽어온 값을 미리
알고 있으니까..)
이것만 읽는다면 하드웨어의 연결이나 타이밍은 95퍼센트 완벽하다고
믿을수 있다. 이후는 소프트웨어에서 해결할 문제만 남는다.

모든 플래시는 각각의 고유한 ID가 있다. 우선 제조사번호와 디바이스
번호(크기,타입)등이 반드시 있다.

스트라타 메뉴얼의 Table-4 (12-page)를 보면 Command가 나와있다.
부트블럭은 Table-5 (15-page)이다.
그중 Read Identifier Codes 란 명령어를 보자 해석하면 기냥 ID읽기 이다.

#define BASE_ADDR
unsigned short *fadr; // 플래시의 주소

처음 할일은 플래시의 상태를 특별한 상태로 변경하는 것이다.
명령에 따라 상태는 변경되며 명령이 끝나면 다시 정상상태(읽기)로
바꾸어 준다. 다시한번 설명하면 최초의 플래시 상태는 읽기만 가능한
상태이다, 플래시이기 때문에 쓰는 동작은 금지되어 있다.

ID읽기 커멘드는 0x90이다.

//=== 명령어 ====//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x90; // command Read ID

커멘드 줬으면 ID를 읽어보자

//=== 명령어에 따른 부가적인 행동 ===//
fadr = BASE_ADDR+0*2;// 제조사코드가있는 주소
(제조사)= *fadr;

fadr = BASE_ADDR+1*2;// 플래시의 디바이스코드가 있는 주소
(코드) = *fadr; // 코드값을 보고 크기를 분석한다.

커멘드를 끝낸다.(반드시 이동작을 해야만 한다.)

//=== 명령 끝 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0xff; // 읽기모드로 변경한다.

너무 쉽다. 여기까지는..

!!!!!!!! 주의 !!!!!!!!!!!!

주의) BASE_ADDR + offset * 2 라는 것을 주의하자
offset은 메뉴얼에 명시된 주소이다. 이것에 2를 곱하는 이유는
메뉴얼에 나온 주소는 Word 단위의 주소이기 때문이다.


2.2.2 스트라타 플래시 정보읽기
이명령은 플래시의 쓰기버퍼크기, 지우기 시간, 쓰기(퓨징)시간,
이때 소모되는 전류와 필요한 전압등이 기재되어 있다.

//=== 명령어 ====//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x98; // command Read Query

//=== 명령어에 따른 부가적인 행동 ===//
fadr = BASE_ADDR+0x27*2;// 플래시의 정확한 크기가 있는 영역
(크기) = 2^(*fadr); // 16MByte 일경우 2^24

fadr = BASE_ADDR+0x2A*2;// 쓰기버퍼의 크기가 있는 영역
(버퍼크기) = 2^(*fadr); // 2^5 = 32

//=== 명령 끝 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0xff; // 읽기모드로 변경한다.


폼나는 프로그램을 작성하려면 이것을 읽어 사용한다.
하지만 여기서는 사용하지 않는다. (시간이 없어서.. ^^)
우리는 스트라타 또는 부트블럭 플래시라는 것과 위에서 읽은 디바이스
크기만을 가지고 작성한다.
스트라타이면 버퍼의 크기는 32-Byte(16-Word) 이다.
부트블럭이면 1-word 이다.


2.2.3 플래시 블럭 쓰기금지/쓰기금지해제
플래시에 데이타를 쓴다는 것은 지워지지 않는 데이타가 필요하기 때문이다.
함부로 지우지 못하도록 하드웨어 핀으로 안전장치를 했으며 소프트웨어적으로는
블럭마다 락(LOCK)을 걸수있다.
플래시를 쓰거나 지우기위해서는 이 락을 풀어야 한다. 물론 락이 걸려있지
않다면 풀이유는 없다.

이것에 대한 사용예는 2.2.4에서 다룬다.

락을 푼이후에 데이타를 쓴후 다시 락을 걸면 데이타가 임의로 지워지는 것을
막을수 있다. 만일 락을 걸고 MTD를 올리면 이 영역은 쓰기가 금지된다.


2.2.4 플래시 블럭 지우기
쓰기위해서 지워야 하는것은 누구나 알고있는 사실이다.
스트라타 플래시 블럭의 크기는 128KByte 이다.
숫자로 보면 0x20000 로 증가하는 주소이다.
부트블럭은 0x10000 증가한다.(64KByte)

부트블럭에서 주의할점은 처음블럭 또는 마지막블럭이 8개로 나누어져 있다는
점이다. 부트블럭 Bottom의 경우
0x00000 0x02000 0x04000 0x06000 .... 0x0E000 // 8개 블럭
0x10000 // 9번째 블럭
0x20000 // 10번째 블럭
:


#define BA // 플래시 블럭주소

블럭에 락이 걸려있는지 확인한다.

//=== 명령어 ====//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
*fadr = 0x70; // command Read Status

//=== 명령어에 따른 부가적인 행동 ===//
fadr = BASE_ADDR+BA+2*2;// 락정보가 있는 주소
(락정보) = *fadr; // 락정보의 d0 비트가 1이면 락이 걸린것이다.

락이 걸려있으면 락을 푼다.

//=== 명령어 ====//
fadr = BASE_ADDR+BA; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x60; // command Set Block Lock-Bit

//=== 명령어에 따른 부가적인 행동 ===//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
*fadr = 0xd0; // 락을 푼다.

//=== 명령 수행이 끝났는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
while (1)
{
// status 레지스터 d7 비트 확인
if (0x0080 & *fadr) break; // SR.7 = 0 이면 busy
}

SR 이라는 것은 status 레지스터를 말하며 위의 코드는 status 레지스터
첫번째 바이트를 읽은 것이다. status 레지스터를 읽기 위해서는 0x70 명령을
줘야 하지만 지우기,쓰기,락 등의 명령를 준 이후에 읽기를 하면 status
레지스터를 읽을수 있다.

이제 블럭을 지운다.

//=== 명령어 ====//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
*fadr = 0x20; // command Block Erase

//=== 명령어에 따른 부가적인 행동 ===//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
*fadr = 0xd0; // command Confirm

//=== 명령 수행이 끝났는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
while (1)
{
// status 레지스터 d7 비트 확인
if (0x0080 & *fadr) break; // SR.7 = 0 이면 busy
}

//=== 상태를 읽어 성공했는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
SR = *fadr;

// SR.3 = 0 프로그램전압 OK
// SR.1 = 0 unlock
// SR.5 = 0 지우기성공
if ( ( SR.3 == 0 ) && ( SR.1 == 0 ) && ( SR.5 == 0 ) )
{
지우기 성공
}
else
{
지우기 실패
}

//=== 상태비트 클리어 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x50; // command Clear Status

//=== 명령 끝 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0xff; // 읽기모드로 변경한다.

지우기 동작은 while 문에서 최대 1초정도 대기하므로 멀티수행을 고려해 본다.
아울러 시간초과 루틴을 필요로 한다.


2.2.4 플래시에 1바이트 또는 1워드 쓰기
쓰기버퍼가 없다면 한개의 워드씩 다음과 같이 써야한다.
스트라타 플래시도 한개씩 쓸수 있다.

#define PA // 쓰고자 하는 주소
#define DA // 쓰려는 데이타

//=== 명령어 ====//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x40; // command Write Word/Byte

//=== 명령어에 따른 부가적인 행동 ===//
fadr = BASE_ADDR+PA; // 쓰고자 하는 주소
*fadr = DA;

//=== 명령 수행이 끝났는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
while (1)
{
// status 레지스터 d7 비트 확인
if (0x0080 & *fadr) break; // SR.7 = 0 이면 busy
}
// 위의 while 문은 최대 2msec 소요된다.

//=== 상태를 읽어 성공했는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
SR = *fadr;

// SR.3 = 0 프로그램전압 OK
// SR.1 = 0 unlock
// SR.4 = 0 프로그램 성공
if ( ( SR.3 == 0 ) && ( SR.1 == 0 ) && ( SR.4 == 0 ) )
{
프로그램 성공
}
else
{
프로그램 실패
}

//=== 상태비트 클리어 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x50; // command Clear Status

//=== 명령 끝 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0xff; // 읽기모드로 변경한다.

여러개의 테이타를 쓰기위해서는 위의 루틴을 반복한다.
속도가 느리겠죠...


2.2.5 스트라타 플래시에 한번에 32바이트 쓰기
스트라타만이 이 명령을 지원한다.

#define LEN 16 // 쓰기버퍼최대 크기
unsigned short buff[LEN];// 데이타버퍼
int wdcnt; // 워드데이타 카운트

//=== 명령어 ====//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
*fadr = 0xE8; // command Write Word/Byte

//=== 명령 수행이 끝났는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
while (1)
{
// status 레지스터 d7 비트 확인
if (0x0080 & *fadr) break; // SR.7 = 0 이면 busy
}

//=== 버퍼에 쓸 갯수를 알려준다 ===//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
wdcnt = 16; // 임의로 최대값을 넣었다.
wdcnt --; // 0 이면 한개의 워드이다.
*fadr = count;

버퍼의 크기는 32-byte 이므로 16-word 가 된다.
그러므로 wdcnt 는 0 ~ 15 사이의 값이 된다.

//=== 카운트만큼 쓴다 ===//
fadr = PA; // 쓰려는 첫번째 주소
for (idx=0; idx<wdcnt; idx++)
{
*fadr = buff[idx];
fadr += 2; // 16비트 버스이므로 2씩 증가한다.
}

//=== 프로그램 명령을 준다===//
fadr = BASE_ADDR+BA; // 플래시의 블럭주소
*fadr = 0xd0; // command Confirm

//=== 명령 수행이 끝났는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
while (1)
{
// status 레지스터 d7 비트 확인
if (0x0080 & *fadr) break; // SR.7 = 0 이면 busy
}
// 위의 while 문은 최대 2msec 소요된다.

//=== 상태를 읽어 성공했는지 확인 ===//
fadr = BASE_ADDR; // status 레지스터 첫번째 주소
SR = *fadr;

// SR.3 = 0 프로그램전압 OK
// SR.1 = 0 unlock
// SR.4 = 0 프로그램 성공
if ( ( SR.3 == 0 ) && ( SR.1 == 0 ) && ( SR.4 == 0 ) )
{
프로그램 성공
}
else
{
프로그램 실패
}

//=== 상태비트 클리어 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0x50; // command Clear Status

//=== 명령 끝 ===//
fadr = BASE_ADDR; // 플래시의 CS가 움직이는 영역이면 된다.
*fadr = 0xff; // 읽기모드로 변경한다.

여러개의 테이타를 쓰기위해서는 위의 루틴을 반복한다.
다시한번 쓰기 위해서는 블럭이 지워져 있어야 한다.

2.2.6 기타의 명령어들
그외에 몇개의 명령이 있지만 설명하지 않겠다.
메뉴얼을 보면 나와있으니 필요하면 참고한다.


이번 강좌에 기술된 내용은 모두 메뉴얼에 나온것이다. 메뉴얼 뒷쪽에 플로워
챠트가 있으니 확인한다.
인텔플래시가 아닌 다른 플래시도 이와 비슷한 형식을 따른다.
직접 제어해보지 안는 이상 확실한 이해는 어려울 것이다. 시간이 되면 직접
코딩하여 플래시에 데이타를 쓰자,


듣고싶은(?) 강좌가 있다면 답글을 주기 바랍니다.. 아는 내용이면 이후의 강좌에
포함하도록 하죠..
으 2시간 걸렸다..... ㅡ.ㅡ


다음 강좌는 NAND형에 대해 알아보겠음당

[소유권]
이 문서 지적 소유권은 (주)제이닷디엔티에 있다.
문서에 관련된 문의 사항이 있다면 freefrug@falinux.com으로 연락 바란다