PAGED память на HCS08 / HCS12
С AC96/AC128 в MultiProg всё в порядке — чтение, запись, verify и TRIM работают «из коробки», MultiProg сам разберется со страничной памятью. Никаких ручных действий с PPAGE для повседневной работы не требуется.
Эта статья — обучающая для тех, кто хочет разобраться, как устроена страничная память на HCS08, почему дампы USBDM и MultiProg выглядят по-разному, и как при необходимости конвертировать прошивки между двумя форматами.
Наверное, каждый мастер-электронщик взрывал мозг, изучая Memory Map и устройство страничной памяти (PAGEd memory) при работе с семействами МК (МК — микроконтроллер) HCS08 / HCS12.
И хотя эти МК уже старенькие и их эпоха ушла, мы всё равно периодически с ними сталкиваемся. Поэтому решили (в том числе для себя, чтобы не забыть) основательно разобрать вопрос в этой стат ье.
Все мы привыкли к линейной памяти: по адресу 0x0000 лежит один-единственный байт, по 0x0001 — следующий, по 0x0002 — следующий за ним, и так подряд, пока память не кончится. Как в обычной книге: на странице 5 всегда то, что написано на странице 5, и чтобы туда попасть, вы просто открываете книгу на этой странице. Адрес — и есть указатель на байт, никаких посредников между ними.
Линейная организация памяти логична и последовательна, поэтому психологически воспринимается проще.
0. Что это такое и зачем оно здесь?
10 лет назад, когда я впервые столкнулся с МК MC9S12C128, я думал, что страничная память — это какая-то крутая фича, смысл которой понятен только избранным. Ну то есть как это работает — понятно, а зачем — понимают только супер-разработчики.
Занимаясь этим семейством МК последние месяцы, могу с уверенностью сказать: это костыль, который по сути закрывает проблему производителя, никаких фичей здесь нет.
В этой статье будем часто обращаться к Application Note AN3730 — собственно, там производитель прямо и пишет:
«Memory paging provides digital systems with greater memory addressing capabilities without expanding basic architecture resources, such as bus size or address pointers.» — AN3730 §1, первый абзац.
В переводе: «чтобы не пришлось расширять шину и менять указатель — то есть не менять архитектуру, — но выпустить МК с большим объёмом памяти, мы придумали интересную технологию». Решение было продиктовано исключительно задачами производителя.
1. Суть-то в чём?
Суть — в двойной системе координат. У страниц (PAGE) нет реального адреса, обратиться к ним можно только через специальный механизм. Одна система координат — обычная, линейная: там лежат RAM, регистры, и какие-то страницы в неё тоже попадают (кусками). Во второй системе страницы существуют, но в общей карте памяти их фактически нет — есть только их части, выгл ядывающие через окно PPAGE.
Из-за этого каждый разработчик программатора-софта может по-своему задать этим разделам адреса, разбивку и т.п. Отсюда и проблемы с файлами прошивок — единого стандарта по факту нет.
Если вы хоть раз читали MC9S08AC128 разными программаторами, то заметили: файлы дампов выглядят совершенно по-разному. МК один, содержимое одно, а адреса в файле — другие.
Из-за двойной системы координат начинаются приколы. Следите за руками: в линейном пространстве с 0x20F0 идёт кусок 7952 байта из страницы 0.
Это рисунок 4-1 из MC9S08AC128 Reference Manual. Половина электронщиков, впервые на него глядя, делает вывод, что Page 0 начинается с 0x20F0. Ошибка в том, что Page 0 существует отдельно — у неё, как и у остальных страниц, 16 КБ. Просто в линейном пространстве у неё «торчит» хвост на 7952 байта (видимо особенности производства).

Самая близкая бытовая аналогия — застройка. Живут 3 семьи (по 16 КБ на семью) в своих домах: всё у них хорошо, есть номера домов и отдельный подъезд — это линейная память. А ещё 5 семей (тоже по 16 КБ на семью) застройщик поселил в одну многоэтажку, к которой ведёт одна дорога и пара тропинок. По бумагам живёт 8 семей — всё здорово.
Младшие модели (всё, у которых флеша до ~64 КБ) укладываются в диапазон 16-битного указателя ядра (адресует до 0xFFFF) и работают как обычная линейная память. А старшие — те, у которых 96 или 128 КБ флеша: AC96/AC128, QE128, DZ128 и им подобные — выходят за то, что покрывает указатель, но память-то всё-таки есть. Вот тут страничная память и закрывает для производителя задачу адресации.
Получается, нельзя просто пройти подряд. В обычной линейной памяти можно читать байт 1, 2, 3, … одним непрерывным движением, как страницы книги. А со старшими, страничными так не получится — нужно сначала настроить окно на выдачу конкретной страницы.
У современного микроконтроллера (32-битный Cortex-M0+ со 128 КБ флеша) этих проблем нет — вы адресуете байт 47 000 как «байт 47 000», точка. Страничный доступ сегодня жив разве что в EEPROM-чипах; в современных МК такой дизайн уже не встречается.
2. Как она организована

Что мы видим на этой картинке:
0x8000–0xBFFF— это окно PPAGE (помните аналогию выше про одну дорогу?). ПослеPPAGE = NCPU видит в этом окне физическую страницуN(16 КБ).- Фиксированные окна
0x4000–0x7FFF(страница 1) и0xC000–0xFFFF(страница 3) — комбинированные: страницы 1 и 3 одновременно «висят» в обеих системах координат — их видно и напрямую через свои фиксированные о кна, и через окно PPAGE (приPPAGE = 1иPPAGE = 3соответственно). Поэтому всё, что традиционно живёт в верхней области — вектор сброса0xFFFE/F, байт защитыNVOPT@0xFFBF, байт подстройки0xFFBE— физически принадлежит странице 3, но CPU видит эти байты «напрямую», как в обычной линейной памяти. В сумме в прямом CPU-виде на AC96/AC128 «торчат» 2,5 страницы: хвост страницы 0 (0x20F0–0x3FFF, нижние 8432 байта затенены RAM и регистрами), вся страница 1 и вся страница 3. Остальные — только через окно PPAGE.

Регистр PPAGE — это «селектор страницы»: записали в него номер, и в окне PPAGE видно ту страницу. Сменили номер — увидели другую. На рисунке выше: PPAGE = 1 → CPU видит страницу 1, PPAGE = 7 → CPU видит страницу 7.
PPAGE на AC96/AC128 — это 3-битное поле (значения 0..7), то есть максимум адресуется 8 × 16 КБ = 128 КБ флеша. AC96 просто короче: страницы 0..5 = 96 КБ.
3. Про совместимость прошивок
МК — это одно, а прошивки и форматы с учётом всей этой уникальной технологии — отдельная головная боль. Существуют два конкурирующих формата раскладки paged-флеша в S-record, и оба используются.
«S19 с PPAGE в старшем байте» — USBDM Memory Dump
USBDM Memory Dump при сохранении страничной памяти генерирует 24-битные адреса: старший байт = PPAGE, младшие 16 бит = вид со стороны CPU внутри окна PPAGE:
address_in_file = (PPAGE << 16) | cpu_addr // cpu_addr в 0x8000..0xBFFF
Страница N уходит в фай л по адресам 0xN8000..0xNBFFF. Родной USBDM использует тот же формат при дампе MC9S08AC128 — на выходе получается S19, у которого 8 page-slot'ов по 64 КБ размазаны по адресам 0x08000..0x7BFFF (≈ 475 КБ занятого диапазона из ≈ 512 КБ потенциальных), притом что реальных данных в файле всего 128 КБ. В каждом слоте только верхние 16 КБ (0xN8000..0xNBFFF) — данные, остальные 48 КБ — пустота: нижние 32 КБ (0xN0000..0xN7FFF) и верхние 16 КБ (0xNC000..0xNFFFF, которые пересеклись бы с фиксированным окном 0xC000..0xFFFF).
«Плоская линейная» MultiProg
Мы используем другой формат: сложить страницы плотно, одну за другой, начиная с 0x8000, без дыр:
address_in_file = 0x8000 + (PPAGE * 0x4000) + (cpu_addr - 0x8000)
Дамп AC128 в этом формате — это ровно 128 КБ непрерывных данных от 0x8000 до 0x27FFF в порядке физических страниц. MultiProg использует именно его, чтобы прошивки байт-в-байт совпадали с Orange 5: записал в одной программе — прочитал в другой, и получаешь те же байты по тем же смещениям.
Сравнение адресов
Один и тот же MC9S08AC128, прочитанный двумя разными программами, даёт:
| Страница | Родной USBDM (PPAGE в старшем байте) | MultiProg (плоский) |
|---|---|---|
| 0 | 0x08000 – 0x0BFFF | 0x08000 – 0x0BFFF |
| 1 | 0x18000 – 0x1BFFF | 0x0C000 – 0x0FFFF |
| 2 | 0x28000 – 0x2BFFF | 0x10000 – 0x13FFF |
| 3 | 0x38000 – 0x3BFFF | 0x14000 – 0x17FFF |
| 4 | 0x48000 – 0x4BFFF | 0x18000 – 0x1BFFF |
| 5 | 0x58000 – 0x5BFFF | 0x1C000 – 0x1FFFF |
| 6 | 0x68000 – 0x6BFFF | 0x20000 – 0x23FFF |
| 7 | 0x78000 – 0x7BFFF | 0x24000 – 0x27FFF |