Инструменты пользователя

Инструменты сайта


prog:spec:ext_ram_1986be8t

Запускаем отладку из внешней памяти ОЗУ на отладочной плате для МК 1986ВЕ8Т без FLM

МК 1986ВЕ8Т может работать в режиме микропроцессора и выполнять программу из внешней памяти, отладка в данном режиме работы также возможна. При этом для загрузки программы во внешнюю память ПЗУ, подключенную к контроллеру внешней шины, необходим FLM файл с алгоритмом загрузки. Но, если по внешней шине подключена память ОЗУ, как это, например, сделано на отладочной плате для МК 1986ВЕ8Т, то для записи программы специальный алгоритм не требуется, а потому можно FLM файл не использовать.

В данной статье мы рассмотрим, как загружать программу во внешнюю память ОЗУ в среде Keil без использования FLM.

Настройка проекта в Keil

В этой статье было описано, как загрузить и запустить программу во внутреннем ОЗУ. Вся настройка проекта сводилась к тому, что необходимо было указать адреса внутреннего ОЗУ для размещения программы (IROM и IRAM), настроить файл инициализации, в котором устанавливались указатель стека, счётчик команд и базовый адрес таблицы векторов прерываний. После этого при переходе в режим Debug (Ctrl+F5) программа загружалась по внутреннюю память ОЗУ и запускалась отладка.

Подробный процесс запуска режима отладки описан на сайте Keil, пункт "Sequence of Execution, и состоит из следующих действий:

1) Если установлен пункт Options for Target→Debug "Load application at Startup", то происходит загрузка скомпилированной программы по адресам IROM, указанным в настройках проекта.

2) Восстановление параметров режима отладки.

3) Выполнение команд в файле инициализации (*.ini).

4) Запуск выполнения программы.

Для отладки из внешней памяти в Keil необходимо указать адреса внешней памяти ОЗУ для размещения программы:

ext_ram.jpg

Так как после сброса МК внешняя шина ещё не настроена и доступа к внешней ОЗУ нет, то загрузка программы в пункте 1 выполнена не будет, поэтому галочку Options for Target→Debug "Load appliacation at Startup" необходимо снять.

load_app.jpg

Загрузка программы во внешнее ОЗУ будем выполнять с использованием файла инициализации (*.ini), который подключается в настройках проекта (рисунок выше). Для этого в данном файле необходимо сначала настроить внешнюю шину, а затем загрузить программу во внешнее ОЗУ. В Keil для первоначальной инициализации МК предусмотрены специальные команды, которые можно использовать в файле *.ini.

Для настройки внешней шины нам понадобится команда _WDWORD, позволяющая записывать 32-разрядные слова по указанному адресу. Таким образом, мы сможем записать необходимые значения в регистры контроллера портов ввода-вывода и регистры контроллера внешней шины.

После того, как внешняя шина настроена, и можно обращаться к внешней памяти, выполним загрузку нашей программы. Для это воспользуемся встроенной командой LOAD.

В конце инициализации устанавливаем указатель стека и счётчик команд, считав значение с таблицы векторов прерываний (чтение выполняется уже из внешней ОЗУ), а также указываем базовый адрес таблицы векторов прерываний. После можно запускать выполнение программы, делается это с помощью команды Go, она же “g”.

Таким образом загрузка программы во внешнее ОЗУ и её дальнейшее выполнение будет осуществляться при запуске режима отладки.

Отладка программы для режима EXTBUS_8_ECC+JA(JB)

Проект HelloWorld, настроенный на запуск в режиме отладки из внешней шины для режима "EXTBUS_8_ECC+JA(JB)" можно скачать здесь.

В режиме запуска "EXTBUS_8_ECC+JA(JB)" (MODE[7:0]=11000011) для взаимодействия с микросхемой памяти МК настраивает шину данных D[7:0], шину адреса А[15:0] и сигнал nOE (сигнал nCE не формируется и должен быть задан отдельно на плате). При этом контроллер внешней шины переводится в режим работы с последовательной организацией ECC с базового адреса 0x1000_9000, запуск осуществляется с адреса 0x1000_0000.

Для отладки программы необходимо в файле инициализации выставить такие же настройки внешней шины, как и при старте в "EXTBUS_8_ECC+JA(JB)", кроме сигнала nWE (PD24), который необходимо настроить, так как используется операция записи.

Микросхема 1645РУ5У имеет шину адреса А[18:0], однако МК в режиме EXTBUS_8_ECC настраивает шину адреса А[15:0], причём ECC имеет базовый адрес 0x1000_9000, поэтому максимальный доступный объём памяти программы составляет 36 Кбайт, адресный диапазон составляет от 0x1000_0000 до 0x1000_9000. Для работы с большим объёмом памяти необходимо использовать режим EXTBUS_CFG.

Получившийся файл инициализации:

Start_EXTBUS_8_ECC.ini
FUNC void Setup (unsigned int region) {
  region &= 0xFFFFF000;
  SP = _RDWORD(region);                          // Setup Stack Pointer
  PC = _RDWORD(region + 4);                      // Setup Program Counter
  _WDWORD(0xE000ED08, region);                   // Setup Vector Table Offset Register
}

FUNC void Setup_EBC (unsigned int region) {

  _WDWORD(0x40000000, 0x8555AAA1); // CLK_CNTR->KEY = 0x8555AAA1;

  _WDWORD(0x4000000C, 0x00038000); /* CLK_CNTR->PER0_CLK = CLKCTRL_PER0_CLK_MDR_PORTC_EN | 
                                                           CLKCTRL_PER0_CLK_MDR_PORTD_EN |
                                                           CLKCTRL_PER0_CLK_MDR_PORTE_EN;  */

  _WDWORD(0x40005000, 0x8555AAA1); // EXT_BUS_CNTR->KEY = 0x8555AAA1;
  _WDWORD(0x40005024, 0x10009000); // EXT_BUS_CNTR->RGN0_ECCBASE = 0x10009000;
  _WDWORD(0x40005004, 0x04440427); /* EXT_BUS_CNTR->RGN0_CNTRL =  1 << EBC_READ32_pos    | 4 << EBC_WS_HOLD_pos  |
                                                                  4 << EBC_WS_SETUP_pos  | 4 << EBC_WS_ACTIVE_pos|
                                                                  2 << EBC_MODE_pos      | 1 << EBC_ECCMODE_pos  |
                                                                  EB_CNTR_ECCEN          | EB_CNTR_EN; */
    
  _WDWORD(0x40082000, 0x8555AAA1); // PORTC->KEY = 0x8555AAA1;
  _WDWORD(0x40083000, 0x8555AAA1); // PORTD->KEY = 0x8555AAA1;
  _WDWORD(0x40084000, 0x8555AAA1); // PORTE->KEY = 0x8555AAA1;
  
  /* A[1:0] (PC[31:30]) */
  _WDWORD(0x40082024, 0x22000000); // PORTC->SFUNC[3] = FUNCVAL(31, 2) | FUNCVAL(30, 2);
  _WDWORD(0x40082038, 0xC0000000); // PORTC->SANALOG = PORT_Pin_31 | PORT_Pin_30; 
  _WDWORD(0x4008205C, 0xF0000000); // PORTC->SPWR[1] = PWRVAL(31, 3) | PWRVAL(30, 3);

  /* A[15:2] (PD[13:0]), D[1:0] (PD[31:30]), nOE (PD23), nWE (PD24) */
  _WDWORD(0x40083018, 0x22222222); /* PORTD->SFUNC[0] = FUNCVAL(7, 2) | FUNCVAL(6, 2) | FUNCVAL(5, 2) |
                                                        FUNCVAL(4, 2) | FUNCVAL(3, 2) | FUNCVAL(2, 2) |
                                                        FUNCVAL(1, 2) | FUNCVAL(0, 2); */
  _WDWORD(0x4008301C, 0x00222222); /* PORTD->SFUNC[1] = FUNCVAL(13, 2) | FUNCVAL(12, 2) | FUNCVAL(11, 2)|
                                                        FUNCVAL(10, 2) | FUNCVAL(9, 2)  | FUNCVAL(8, 2); */
  _WDWORD(0x40083020, 0x20000000); /* PORTD->SFUNC[2] = FUNCVAL(23, 2); */
  _WDWORD(0x40083024, 0x22000002); /* PORTD->SFUNC[3] = FUNCVAL(31, 2) | FUNCVAL(30, 2) | FUNCVAL(24, 2); */
  _WDWORD(0x40083038, 0xC1803FFF); /* PORTD->SANALOG = PORT_Pin_31 | PORT_Pin_30 | PORT_Pin_24 | PORT_Pin_23 | PORT_Pin_13 |
                                                       PORT_Pin_12 | PORT_Pin_11 | PORT_Pin_10 | PORT_Pin_9  | PORT_Pin_8  |
                                                       PORT_Pin_7  | PORT_Pin_6  | PORT_Pin_5  | PORT_Pin_4  | PORT_Pin_3  |
                                                       PORT_Pin_2  | PORT_Pin_1  | PORT_Pin_0; */
  _WDWORD(0x40083058, 0x0FFFFFFF); /* PORTD->SPWR[0] = PWRVAL(13, 3) | PWRVAL(12, 3) | PWRVAL(11, 3) |
                                                       PWRVAL(10, 3) | PWRVAL(9, 3)  | PWRVAL(8, 3)  |
                                                       PWRVAL(7 , 3) | PWRVAL(6, 3)  | PWRVAL(5, 3)  |
                                                       PWRVAL(4 , 3) | PWRVAL(3, 3)  | PWRVAL(2, 3)  |
                                                       PWRVAL(1 , 3) | PWRVAL(0, 3); */
  _WDWORD(0x4008305C, 0xF003C000); // PORTD->SPWR[1] = PWRVAL(31, 3) | PWRVAL(30, 3) | PWRVAL(24, 3) | PWRVAL(23, 3);	

  /* D[7:2] (PE[5:0]) */
  _WDWORD(0x40084018, 0x00222222); /* PORTE->SFUNC[0] = FUNCVAL(5, 2) | FUNCVAL(4, 2) | FUNCVAL(3, 2) |
                                                        FUNCVAL(2, 2) | FUNCVAL(1, 2) | FUNCVAL(0, 2); */
  _WDWORD(0x40084038, 0x0000003F); // PORTE->SANALOG = PORT_Pin_5 | PORT_Pin_4 | PORT_Pin_3 | PORT_Pin_2 | PORT_Pin_1 | PORT_Pin_0; 
  _WDWORD(0x40084058, 0x00000FFF); /* PORTE->SPWR[0] = PWRVAL(5, 3) | PWRVAL(4, 3) | PWRVAL(3, 3) |
                                                       PWRVAL(2, 3) | PWRVAL(1, 3) | PWRVAL(0, 3); */
}

RESET                  // Reset MCU
Setup_EBC(0x10000000); // Config EBC
LOAD $L%L INCREMENTAL  // Load program to external RAM
Setup(0x10000000);     // Setup SP, PC, VT
g ,main                // Go to main

Если на плате установлен режим EXTBUS_8_ECC+JA(JB), то после загрузки программы во внешнюю память, можно нажать кнопки reset, после чего МК сбросится и запустится с внешней памяти. Можно заметить, что после сброса светодиод стал мигать с меньшей частотой. Это вызвано тем, что после сброса для контроллера внешней шины установлены достаточно большие времена обмена (ACTIVE, SETUP, HOLD), а потому инструкции считываются с меньшей скоростью.

Отладка программы для режима EXTBUS_CFG+JA(JB)

Проект HelloWorld, настроенный на запуск в режиме отладки из внешней шины для режима "EXTBUS_CFG+JA(JB)" можно скачать здесь.

В режиме "EXTBUS_CFG+JA(JB)" (MODE[7:0]=10100101) для загрузки конфигурационных бит CFG[9:0] МК настраивает шину данных D[7:0], шину адреса А[10:3] и сигнал nOE (сигнал nCE не формируется и должен задаваться отдельно на плате). На основании считанных бит CFGx МК устанавливает необходимые настройки контроллера внешней шины, расширяет шину адреса до A[20:0], после чего происходит запуск с адреса 0x1000_0000.

На отладочной плате для 1986ВЕ8Т установлена 8-разрядная микросхема памяти ОЗУ 1645РУ5У, которую мы и будем использовать для отладки. Поэтому зададим конфигурационные биты CFG таким образом, чтобы шина данных была 8-разрядной (CFG0 = 1), с последовательным ECC (CFG1 = 2), базовый адрес выберем как и в предыдущем пункте - 0x10009000 (CFG[9:2]). Считывание конфигурационных бит CFG производится по адресам 0x1000_0400, 0x1000_0408, … 0x1000_0448. В среде Keil данные значения могут быть прописаны с помощью команд в любом файле программы, например, в main.c:

const uint8_t Cfg_0 __attribute__((at(0x10000400))) = 0x71; // CFG0 - шина данных	
const uint8_t Cfg_1 __attribute__((at(0x10000408))) = 0xB2; // CFG1 - контроль ECC
const uint8_t Cfg_2 __attribute__((at(0x10000410))) = 0x00; // CFG2 - BASE_ECC[3:0]	
const uint8_t Cfg_3 __attribute__((at(0x10000418))) = 0x00; // CFG3 - BASE_ECC[7:4]
const uint8_t Cfg_4 __attribute__((at(0x10000420))) = 0x00; // CFG3 - BASE_ECC[11:8]	
const uint8_t Cfg_5 __attribute__((at(0x10000428))) = 0x99; // CFG3 - BASE_ECC[15:12]
const uint8_t Cfg_6 __attribute__((at(0x10000430))) = 0x00; // CFG3 - BASE_ECC[19:16]	
const uint8_t Cfg_7 __attribute__((at(0x10000438))) = 0x00; // CFG3 - BASE_ECC[23:20]
const uint8_t Cfg_8 __attribute__((at(0x10000440))) = 0x00; // CFG3 - BASE_ECC[27:24]	
const uint8_t Cfg_9 __attribute__((at(0x10000448))) = 0x71; // CFG3 - BASE_ECC[31:28]

Для конфигурационных бит CFG также используется кодирование ECC, поэтому при записи конфигурационных бит необходимо добавить биты ECC, при этом каждое конфигурационное слово представляет собой байт данных DATA[7:0] = ECC[3:0]+CFGx[3:0], где ECC[3:0] - старший полубайт, а CFGx[3:0] - младший полубайт. Необходимы биты ECC[3:0] можно рассчитать либо с помощью специального алгоритма, либо по таблице, в зависимости от выбранных бит CFGx[3:0]. Подробнее про это описано в статье.

Микросхема памяти 1645РУ5У подключена к шине адреса А[18:0], поэтому мы возьмём файл инициализации из предыдущего пункта и добавим конфигурацию выводов для задания адресов А[18:16] - PD[16:14] (в предыдущем пункте шина адреса была A[15:0]).

Получившийся файл инициализации:

Start_EXTBUS_CFG.ini
FUNC void Setup (unsigned int region) {
  region &= 0xFFFFF000;
  SP = _RDWORD(region);                          // Setup Stack Pointer
  PC = _RDWORD(region + 4);                      // Setup Program Counter
  _WDWORD(0xE000ED08, region);                   // Setup Vector Table Offset Register
}

FUNC void Setup_EBC (unsigned int region) {

  _WDWORD(0x40000000, 0x8555AAA1); // CLK_CNTR->KEY = 0x8555AAA1;

  _WDWORD(0x4000000C, 0x00038000); /* CLK_CNTR->PER0_CLK = CLKCTRL_PER0_CLK_MDR_PORTC_EN | 
                                                           CLKCTRL_PER0_CLK_MDR_PORTD_EN |
                                                           CLKCTRL_PER0_CLK_MDR_PORTE_EN;  */

  _WDWORD(0x40005000, 0x8555AAA1); // EXT_BUS_CNTR->KEY = 0x8555AAA1;
  _WDWORD(0x40005024, 0x10009000); // EXT_BUS_CNTR->RGN0_ECCBASE = 0x10009000;
  _WDWORD(0x40005004, 0x04440427); /* EXT_BUS_CNTR->RGN0_CNTRL =  1 << EBC_READ32_pos    | 4 << EBC_WS_HOLD_pos  |
                                                                  4 << EBC_WS_SETUP_pos  | 4 << EBC_WS_ACTIVE_pos|
                                                                  2 << EBC_MODE_pos      | 1 << EBC_ECCMODE_pos  |
                                                                  EB_CNTR_ECCEN          | EB_CNTR_EN; */
    
  _WDWORD(0x40082000, 0x8555AAA1); // PORTC->KEY = 0x8555AAA1;
  _WDWORD(0x40083000, 0x8555AAA1); // PORTD->KEY = 0x8555AAA1;
  _WDWORD(0x40084000, 0x8555AAA1); // PORTE->KEY = 0x8555AAA1;

  /* A[1:0] (PC[31:30]) */
  _WDWORD(0x40082024, 0x22000000); // PORTC->SFUNC[3] = FUNCVAL(31, 2) | FUNCVAL(30, 2);
  _WDWORD(0x40082038, 0xC0000000); // PORTC->SANALOG = PORT_Pin_31 | PORT_Pin_30; 
  _WDWORD(0x4008205C, 0xF0000000); // PORTC->SPWR[1] = PWRVAL(31, 3) | PWRVAL(30, 3);

  /* A[18:2] (PD[16:0]), D[1:0] (PD[31:30]), nOE (PD23), nWE (PD24) */
  _WDWORD(0x40083018, 0x22222222); /* PORTD->SFUNC[0] = FUNCVAL(7, 2) | FUNCVAL(6, 2) | FUNCVAL(5, 2) |
                                                        FUNCVAL(4, 2) | FUNCVAL(3, 2) | FUNCVAL(2, 2) |
                                                        FUNCVAL(1, 2) | FUNCVAL(0, 2); */
  _WDWORD(0x4008301C, 0x22222222); /* PORTD->SFUNC[1] = FUNCVAL(15, 2) | FUNCVAL(14, 2) | FUNCVAL(13, 2) |
                                                        FUNCVAL(12, 2) | FUNCVAL(11, 2) | FUNCVAL(10, 2) |
                                                        FUNCVAL(9, 2)  | FUNCVAL(8, 2); */
  _WDWORD(0x40083020, 0x20000002); /* PORTD->SFUNC[2] = FUNCVAL(23, 2) | FUNCVAL(16, 2); */
  _WDWORD(0x40083024, 0x22000002); /* PORTD->SFUNC[3] = FUNCVAL(31, 2) | FUNCVAL(30, 2) | FUNCVAL(24, 2); */
  _WDWORD(0x40083038, 0xC181FFFF); /* PORTD->SANALOG = PORT_Pin_31 | PORT_Pin_30 | PORT_Pin_24 | PORT_Pin_23 | PORT_Pin_16 |
                                                       PORT_Pin_15 | PORT_Pin_14 | PORT_Pin_13 | PORT_Pin_12 | PORT_Pin_11 |
                                                       PORT_Pin_10 | PORT_Pin_9  | PORT_Pin_8  | PORT_Pin_7  | PORT_Pin_6  |
                                                       PORT_Pin_5  | PORT_Pin_4  | PORT_Pin_3  | PORT_Pin_2  | PORT_Pin_1  |
                                                       PORT_Pin_0; */
  _WDWORD(0x40083058, 0xFFFFFFFF); /* PORTD->SPWR[0] = PWRVAL(15, 3) | PWRVAL(14, 3) | PWRVAL(13, 3) |
                                                       PWRVAL(12, 3) | PWRVAL(11, 3) | PWRVAL(10, 3) |
                                                       PWRVAL(9, 3)  | PWRVAL(8, 3)  | PWRVAL(7, 3)  |
                                                       PWRVAL(6 , 3) | PWRVAL(5, 3)  | PWRVAL(4, 3)  |
                                                       PWRVAL(3 , 3) | PWRVAL(2, 3)  | PWRVAL(1, 3)  |
                                                       PWRVAL(0 , 3); */
  _WDWORD(0x4008305C, 0xF003C003); // PORTD->SPWR[1] = PWRVAL(31, 3) | PWRVAL(30, 3) | PWRVAL(24, 3) | PWRVAL(23, 3)| PWRVAL(16, 3);	

  /* D[7:2] (PE[5:0]) */
  _WDWORD(0x40084018, 0x00222222); /* PORTE->SFUNC[0] = FUNCVAL(5, 2) | FUNCVAL(4, 2) | FUNCVAL(3, 2) |
                                                        FUNCVAL(2, 2) | FUNCVAL(1, 2) | FUNCVAL(0, 2); */
  _WDWORD(0x40084038, 0x0000003F); // PORTE->SANALOG = PORT_Pin_5 | PORT_Pin_4 | PORT_Pin_3 | PORT_Pin_2 | PORT_Pin_1 | PORT_Pin_0; 
  _WDWORD(0x40084058, 0x00000FFF); /* PORTE->SPWR[0] = PWRVAL(5, 3) | PWRVAL(4, 3) | PWRVAL(3, 3) |
                                                       PWRVAL(2, 3) | PWRVAL(1, 3) | PWRVAL(0, 3); */
}

RESET                  // Reset MCU
Setup_EBC(0x10000000); // Config EBC
LOAD $L%L INCREMENTAL  // Load program to external RAM
Setup(0x10000000);     // Setup SP, PC, VT
g ,main                // Go to main

Как и в предыдущем случае, если на плате установлен режим EXTBUS_CFG+JA(JB), то после загрузки программы во внешнюю память, можно нажать кнопки reset, после чего МК сбросится и запустится с внешней памяти.

prog/spec/ext_ram_1986be8t.txt · Последние изменения: 2019/05/31 10:28 — vova