======Запускаем отладку из внешней памяти ОЗУ на отладочной плате для МК 1986ВЕ8Т без FLM====== МК 1986ВЕ8Т может работать в режиме микропроцессора и выполнять программу из внешней памяти, отладка в данном режиме работы также возможна. При этом для загрузки программы во внешнюю память ПЗУ, подключенную к контроллеру внешней шины, необходим FLM файл с алгоритмом загрузки. Но, если по внешней шине подключена память ОЗУ, как это, например, сделано на отладочной плате для МК 1986ВЕ8Т, то для записи программы специальный алгоритм не требуется, а потому можно FLM файл не использовать. В данной статье мы рассмотрим, как загружать программу во внешнюю память ОЗУ в среде Keil без использования FLM. =====Настройка проекта в Keil===== В этой [[https://startmilandr.ru/doku.php/prog:start:run_ram_keil |статье]] было описано, как загрузить и запустить программу во внутреннем ОЗУ. Вся настройка проекта сводилась к тому, что необходимо было указать адреса внутреннего ОЗУ для размещения программы (IROM и IRAM), настроить файл инициализации, в котором устанавливались указатель стека, счётчик команд и базовый адрес таблицы векторов прерываний. После этого при переходе в режим Debug (Ctrl+F5) программа загружалась по внутреннюю память ОЗУ и запускалась отладка. Подробный процесс запуска режима отладки описан на [[http://www.keil.com/support/man/docs/uv4/uv4_dg_debug.htm |сайте Keil]], пункт "Sequence of Execution, и состоит из следующих действий: 1) Если установлен пункт Options for Target->Debug "Load application at Startup", то происходит загрузка скомпилированной программы по адресам IROM, указанным в настройках проекта. 2) Восстановление параметров режима отладки. 3) Выполнение команд в файле инициализации (*.ini). 4) Запуск выполнения программы. Для отладки из внешней памяти в Keil необходимо указать адреса внешней памяти ОЗУ для размещения программы: {{prog:extbus:ext_ram.jpg}} Так как после сброса МК внешняя шина ещё не настроена и доступа к внешней ОЗУ нет, то загрузка программы в пункте 1 выполнена не будет, поэтому галочку Options for Target->Debug "Load appliacation at Startup" необходимо снять. {{prog:extbus:load_app.jpg}} Загрузка программы во внешнее ОЗУ будем выполнять с использованием файла инициализации (*.ini), который подключается в настройках проекта (рисунок выше). Для этого в данном файле необходимо сначала настроить внешнюю шину, а затем загрузить программу во внешнее ОЗУ. В Keil для первоначальной инициализации МК предусмотрены [[http://www.keil.com/support/man/docs/uv4/uv4_debug_commands.htm |специальные команды]], которые можно использовать в файле *.ini. Для настройки внешней шины нам понадобится команда [[http://www.keil.com/support/man/docs/uv4/uv4_df_predeffunct.htm |_WDWORD]], позволяющая записывать 32-разрядные слова по указанному адресу. Таким образом, мы сможем записать необходимые значения в регистры контроллера портов ввода-вывода и регистры контроллера внешней шины. После того, как внешняя шина настроена, и можно обращаться к внешней памяти, выполним загрузку нашей программы. Для это воспользуемся встроенной командой [[http://www.keil.com/support/man/docs/uv4/uv4_cm_load.htm |LOAD]]. В конце инициализации устанавливаем указатель стека и счётчик команд, считав значение с таблицы векторов прерываний (чтение выполняется уже из внешней ОЗУ), а также указываем базовый адрес таблицы векторов прерываний. После можно запускать выполнение программы, делается это с помощью команды [[http://www.keil.com/support/man/docs/uv4/uv4_cm_go.htm| Go]], она же “g”. Таким образом загрузка программы во внешнее ОЗУ и её дальнейшее выполнение будет осуществляться при запуске режима отладки. =====Отладка программы для режима EXTBUS_8_ECC+JA(JB)===== Проект HelloWorld, настроенный на запуск в режиме отладки из внешней шины для режима "EXTBUS_8_ECC+JA(JB)" можно скачать [[https://drive.google.com/uc?authuser=0&id=1BVoMb8aMpAuYaG2FZ3sBcOB55WmBjJaN&export=download|здесь]]. В режиме запуска "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. Получившийся файл инициализации: 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)" можно скачать [[https://drive.google.com/uc?authuser=0&id=1R051VzX2tJmrtFPp2b6NHP2StF25zr9S&export=download|здесь]]. В режиме "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]. Подробнее про это описано в [[https://startmilandr.ru/doku.php/prog:spec:hammingcode#%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%B0%D1%8F_%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F_ecc| статье]]. Микросхема памяти 1645РУ5У подключена к шине адреса А[18:0], поэтому мы возьмём файл инициализации из предыдущего пункта и добавим конфигурацию выводов для задания адресов А[18:16] - PD[16:14] (в предыдущем пункте шина адреса была A[15:0]). Получившийся файл инициализации: 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, после чего МК сбросится и запустится с внешней памяти.