======Типы памяти при работе с внешней шиной======
===Адресное пространство в Cortex-M3 (МК 1986ВЕ9х)===
^ Адреса ^ Назначение ^ Тип Памяти ^ XN ^ Описание ^
| 0x0000_0000 - 0x1FFF_FFFF | Code | Normal | - | Код программы и данные |
| 0x2000_0000 - 0x3FFF_FFFF | SRAM | Normal | - | Данные и код программы |
| 0x4000_0000 - 0x5FFF_FFFF | Peripheral | Device | XN | Периферия Milandr |
| 0x6000_0000 - 0x9FFF_FFFF | Ext RAM | Normal | - | Внешнее ОЗУ |
| 0xA000_0000 - 0xDFFF_FFFF | Ext Device | Device | XN | Внешние Устройства |
| 0xE000_0000 - 0xE000_FFFF | Core Bus | Strongly Ordered | - | Периферия ядра - NVIC, SysTimer, регистры ядра |
| 0xE001_0000 - 0xFFFF_FFFF | Reserved | Device | XN | Зарезервировано |
**XN** - //Execute Never//, обращение за инструкциями для исполнения в данные области не возможен.
Доступ к контроллеру внешней шины возможен через различные области адресного пространства. Эти области имеют разные //типы памяти//, которые могут быть изменены в блоке //MPU//. Всего типов памяти три:
* Normal
* Device
* Strongly Ordered
Обращение к внешней шине через эти области происходит с некоторыми нюансами. Предположим, что в коде есть подряд несколько записей во внешнюю память. Например, последовательность из статьи - [[prog:extbus:extbus_rr1_ve1|Работа с Flash памятью 1636РР1У по внешней шине 1986ВЕ91Т]].
// Стирание всей памяти
FlashStatus EraseFullFLASH(void)
{
...
// Командная последовательность EraseFull
HWEXTBUS(0x555) = 0xAAAAAAAA;
HWEXTBUS(0x2AA) = 0x55555555;
HWEXTBUS(0x555) = 0x80808080;
HWEXTBUS(0x555) = 0xAAAAAAAA;
HWEXTBUS(0x2AA) = 0x55555555;
HWEXTBUS(0x555) = 0x10101010
...
}
Каждое обращение на внешнюю шину занимает значительное количество тактов, ведь внешняя шина работает медленнее, чем ядро. Времена работы внешней шины задаются программно в регистрах и подстраиваются под скорость работы внешнего устройства.
Процессор же, получив такую последовательность команд, должен либо остановиться пока не закончится текущая операция на шине (режим **Strongly Ordered**), либо записать обращения в некий внутренний буфер и продолжить выполнение прочих инструкций, не снижая общую производительность.
Затем, команды из буфера (параллельно с работой ядра) будут выводиться на контроллер внешней шины - и здесь есть два варианта:
- Если типа памяти **Normal**, то последовательность исполнения команд из буфера может быть изменена для повышения быстродействия. Не знаю, как происходит оптимизация исполнения в ядре на самом деле, но например, для снижения количества переключений сначала можно выполнить все чтения, а затем все записи. Ведь для переключения режима Read/Write необходимо дополнительное время на переключение сигналов шины nOE и WE, а так-же режима линий данных In/Out. Такой режим работы подходит, например, для чтения внешнего ОЗУ, где очередность выборки данных не имеет значения.
- Если тип памяти **Device**, то последовательность команд сохраняется неизменной. Этот режим необходим там, где последовательность команд является важной.
В данном коде выбор режима, в котором будет работать ядро, определяется старшими разрядами адреса при обращении в адресное пространство.
// Макрос обращения к памяти
#define HWREG(x) (*((volatile uint32_t *)(x)))
// Макрос обращения к внешней шине
#define EXTBUS_START_ADDR 0x60000000 // Normal
или
#define EXTBUS_START_ADDR 0xA0000000 // Device
#define EXTBUS_ADDR(x) (EXTBUS_START_ADDR + ((x) << 2))
#define HWEXTBUS(x) HWREG(EXTBUS_ADDR(x))
Для управлением записью в 1636РР1У необходимо четко соблюдать последовательность команд, поэтому необходимо осуществлять обращения к внешней шине через регион с типом памяти //Device//. То-есть, адреса должны быть в диапазоне начинающимся с 0xA000_0000.