Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Запуск кода из внешней памяти
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
inventor
подскажите мне вот что
имеется арм контроллер
в котором ест 48 килобайт памяти
этого мало для моих задач, я подцепил 4 Мбайт внешней памяти
и хочу что бы старт начался из нее, а не из внутренней SRAM
и чтобы стек там же располагался
как мне это правильно сделать
чтобы стек так же расположить во внешней памяти?

вот у меня есть файлstartup_MYCPU.s
где есть вызов LDR R0, =SystemInit
что я должен сделать внутри System Init
кроме настройки внешней шины адреса, данных, управления?
dimka76
Вы даже марку контроллера не упомянули.

А так вам еще и скрипт линкера подправить надо будет и скопировать вашу программу во внешнюю память.
Отремапить таблицу векторов прерывания.
Obam
Ещё "бензинчику" добавлю (; загрузка в R0 адреса SystemInit ничего не вызовет.
Внешняя память то какая: flash? ram?
golf2109
Цитата(Obam @ Sep 19 2017, 21:38) *
Ещё "бензинчику" добавлю (; загрузка в R0 адреса SystemInit ничего не вызовет.
Внешняя память то какая: flash? ram?

наверное SPI Flash
inventor
Цитата(Obam @ Sep 19 2017, 22:38) *
Ещё "бензинчику" добавлю (; загрузка в R0 адреса SystemInit ничего не вызовет.
Внешняя память то какая: flash? ram?

RAM
контроллер миландр
ну так что мне нужно сделать, по шагам?

THUMB

; Reset Handler
PUBWEAK Reset_Handler
SECTION .text:CODE:NOROOT:REORDER(2)
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0


вот так начинается программа
скрипт линкера такой:

CODE
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x00000000;

/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x00000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x0001ffff;

/* Внутренняя память */
define symbol __ICFEDIT_region_SRAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_SRAM_end__ = 0x20007fff;

/* Внешняя память */
define symbol __ICFEDIT_region_XRAM_start__= 0x60200000;
define symbol __ICFEDIT_region_XRAM_end__ = (0x60200000 + 0x00040000 - 1);

/* Стек и куча */
define symbol __ICFEDIT_size_cstack__ = 0x2000;
define symbol __ICFEDIT_size_heap__ = 0x4000;

/**** End of ICF editor section. ###ICF###*/

define memory mem with size = 4G;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__];
define region SRAM_region = mem:[from __ICFEDIT_region_SRAM_start__ to __ICFEDIT_region_SRAM_end__];
define region XRAM_region = mem:[from __ICFEDIT_region_XRAM_start__ to __ICFEDIT_region_XRAM_end__];


define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };

initialize by copy { readwrite };
do not initialize { section .noinit };

keep { section .intvec };
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };

place in ROM_region { readonly };
place in SRAM_region { readwrite,
block CSTACK,
block HEAP};


/* place in XRAM_region {readwrite}; */
//place in RAM_region{ block HEAP };

Forger
Цитата(inventor @ Sep 20 2017, 09:53) *
ну так что мне нужно сделать, по шагам?

Назвать марку проца, памяти и название среды разработки (хотя не нужно, догадались - IAR).
inventor
неицинициализированные данные я там могу расположить
теперь дело встало за тем, чтобы там жили функции
и данные

CPU milandr 1986ВЕ3
память RAM то же миландр
подклчается через 2 ноги на адреса
то есть адрес RAM A0 идет на адрес A2 проца
то есть обращение 32 разрядное

среда разработки IAR 6.5
Forger
Цитата(inventor @ Sep 20 2017, 09:58) *
1986ВЕ3

В мануале к IAR должно быть расжевано как настраивать скрипт линкера. Полистайте хелп к IAR.
При старте все равно запуск должен производится из внутренней FLASH, в которой будет настраивается контроллер памяти,
после чего копируется исполняемый код из FLASH (код должен быть скомпилирован для работы с определенного адреса) во внешнюю ОЗУ,
делается REMAP векторов, стека и передается управление во внешнее ОЗУ.
Integro
Цитата(inventor @ Sep 20 2017, 09:53) *
RAM
контроллер миландр
ну так что мне нужно сделать, по шагам?


миландр, этого мало, какой?
RAM, интерфейс?

вкратце, если SRAM подключена по штатному интерфейсу и память будет замаплена на адреса рабочего пространства, то проблем не должно быть.
- Обычно в файлах startup.c (.S) вызывается SystemInit там нужно включить инициализацию RAM
- Дальше, зная адреса RAM, добавляем в линкер скрип регион RAM, распологаем там стек

UPD: долго печатал)
inventor
Цитата(Integro @ Sep 20 2017, 10:04) *
миландр, этого мало, какой?
RAM, интерфейс?

вкратце, если SRAM подключена по штатному интерфейсу и память будет замаплена на адреса рабочего пространства, то проблем не должно быть.
- Обычно в файлах startup.c (.S) вызывается SystemInit там нужно включить инициализацию RAM
- Дальше, зная адреса RAM, добавляем в линкер скрип регион RAM, распологаем там стек

UPD: долго печатал)

там еще такое, чтобы внешняя памят заработала
я настраивают выводы для работы с интерфейсом внешней памяти
то есть какие выводы будут шиной данных, какие шиной адреса
а какие управляющие, и кроме этого настраиваю memory region
и задаю число циклов ожидание и пр.
эта функция, как я понимаю, должна быть вызвана до все этих ремапов?

понятно, не видел собщение предыдущее
такой еще вопрос: в SystemInit инициализацию Ram делать
напрямую обращаясь к регистрам или вызываьт функции (использовать стек и пр) ?
Forger
Я бы не стал мудрить с полным переносом кода во внешнюю ОЗУ, а размещал там лишь некоторые функции, которые должны работать оттуда.
IAR поддерживает RAM-функции (читайте хелп к нему).
Тогда нужно лишь настроить правильно скрипт линкера (читайте хелп к IAR) и выбрать нужные функции, которые должны работать во внешней ОЗУ (как это делать тоже есть в хелпе к IAR).
Если стек не конский, то я бы разместил его (и скорее всего вектора) во внутренней ОЗУ, если она работает быстрее внешней ОЗУ. Но вектора можно оставить во FLASH.
Если используется огромная куча (heap), то ее - во внешнюю ОЗУ.
Разумеется, инициализация контроллера памяти для внешней ОЗУ должна выполнять ДО того как обращаетесь к ней.
Integro
Цитата(inventor @ Sep 20 2017, 10:15) *
эта функция, как я понимаю, должна быть вызвана до все этих ремапов?

RAM и все пины нужно настроить до вызова __iar_program_start, тоесть в SystemInit()

Функция __iar_program_start начинает инициализивать "стреду" компилятора, тоесть инициализоровать переменные (секции .bss и .data) если они будут лежать во внешней рам, это критично.
Кроме того, __iar_program_start уже может пользовать стек.

Код
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0


UPD:
Цитата(inventor @ Sep 20 2017, 10:15) *
напрямую обращаясь к регистрам или вызываьт функции (использовать стек и пр) ?

Можно постараться не использовать стек, тоесть напрямую через регистры.
Можно подменить на время инициализции, стек, на нормальную SRAM потом вернуть, делается это через регист SP (MSP)
scifi
Ещё интересный вопрос, где хранится код программы (тот, который большой и никуда не помещается) и как планируете его закачать во внешнее ОЗУ.
Кстати, вот вам ход конём:
Сделать загрузчик и основную программу (2 раздельных проекта). Загрузчик, ясное дело, загружает, а потом передаёт управление основной программе. Там этих заморочек с настройками в разы меньше.
Forger
Для толстых проектов обычно ставят внешнюю FLASH (по SPI) с загружаемым образом (шифрованным-упакованным), который при старке кидается во внешнюю ОЗУ, и из нее запускается.

зы. пока писал этот пост, меня опередили ))
scifi
Цитата(Forger @ Sep 20 2017, 10:48) *
Для толстых проектов обычно ставят внешнюю FLASH (по SPI) с загружаемым образом (шифрованным-упакованным), который при старке кидается во внешнюю ОЗУ, и из нее запускается.

ЕМНИП, FTDI сделал МК, в котором SPI флеш сидит внутри в роли ПЗУ. При старте автоматичски копируется в ОЗУ и запускается.
тыц
inventor
Цитата(scifi @ Sep 20 2017, 10:40) *
Кстати, вот вам ход конём:
Сделать загрузчик и основную программу (2 раздельных проекта). Загрузчик, ясное дело, загружает, а потом передаёт управление основной программе. Там этих заморочек с настройками в разы меньше.

это я уже делал и на блекфине и на stm32
теперь нечто подобное нужно сделать на миландре
scifi
Цитата(inventor @ Sep 20 2017, 10:50) *
это я уже делал и на блекфине и на stm32
теперь нечто подобное нужно сделать на миландре

И за чем же дело стало? laughing.gif
jcxz
Цитата(scifi @ Sep 20 2017, 14:59) *
И за чем же дело стало? laughing.gif

Автор видимо хотел сказать, что для тех МК он находил готовые решения, а тут - не может найти.
Если бы сам делал, то даже вопроса бы такого не возникло "как". laughing.gif
inventor
Цитата(jcxz @ Sep 20 2017, 11:33) *
Автор видимо хотел сказать, что для тех МК он находил готовые решения, а тут - не может найти.
Если бы сам делал, то даже вопроса бы такого не возникло "как". laughing.gif

ну типа того sm.gif
Forger
Цитата(inventor @ Sep 20 2017, 11:40) *
ну типа того sm.gif

Значит, пришло время для самостоятельной работы wink.gif
VladislavS
Значит так. Процессор ваш имеет 128 Кбайт Flash и 48 Кбайт RAM на ядре Cortex-M.
1. Пишете загрузчик, который выполняется из внутренней Flash при старте. В нём инициализируете внешнюю память и копируете в неё из какой-то внешней флэшки основной код полностью.
Затем передаёте управление основной программе
Код
#define APPLICATION_ADDRESS  0xXXXXXXXX
typedef void(*pFunction)(void);
uint32_t app_jump_address = *( uint32_t*) (APPLICATION_ADDRESS + 4);   //извлекаем адрес перехода из вектора Reset
pFunction Jump_To_Application = (pFunction)app_jump_address;                  
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);                   //устанавливаем стек приложения                                          
Jump_To_Application();


2. Основная программа ничего не знает о том что её откуда-то скопировали. Её задача только перенаправить на себя таблицу векторов прерываний.
Код
  #pragma section = ".intvec"
  SCB->VTOR = (uint32_t) __section_begin(".intvec");

А стек я бы расположил всё же во внутренней RAM процессора. 48 кБ для стека это дофига вообще. Да и быстрее эта память обычно.
Forger
Цитата(VladislavS @ Sep 20 2017, 12:12) *
В нём инициализируете внешнюю память и копируете в неё из какой-то внешней флэшки основной код полностью.

Добавлю свои пять копеек: код должен быть зашифрован, если изделие коммерческое.
А оно коммерческое по-любому, т. к. прозвучало ключевое слово - "миландр" sm.gif
Открытые исходники различных шифровальщиков не проблема.

Цитата
48 кБ для стека это дофига вообще.
Дофига - мягко сказано!
Видать, очень толстый проект, коли 48кБ ОЗУ не хватает wink.gif
jcxz
Цитата(VladislavS @ Sep 20 2017, 16:12) *
Значит так. Процессор ваш имеет 128 Кбайт Flash и 48 Кбайт RAM на ядре Cortex-M.
...

Ну вот.... У человека был шанс чему-то разобраться, научиться, может даже стать наконец-то настоящим программистом... и опять ему помешали..... crying.gif
VladislavS
Цитата(Forger @ Sep 20 2017, 12:21) *
Добавлю свои пять копеек: код должен быть зашифрован, если изделие коммерческое.
А оно коммерческое по-любому, т. к. прозвучало ключевое слово - "миландр" sm.gif
Открытые исходники различных шифровальщиков не проблема.

С таким ключевым словом обычно чтобы поиметь доступ к процессору надо в открытый космос выходить sm.gif

Цитата(jcxz @ Sep 20 2017, 12:42) *
Ну вот.... У человека был шанс чему-то разобраться, научиться, может даже стать наконец-то настоящим программистом... и опять ему помешали..... crying.gif

А зачем вам конкуренты? sm.gif
Forger
Цитата(VladislavS @ Sep 20 2017, 13:00) *
С таким ключевым словом обычно чтобы поиметь доступ к процессору надо в открытый космос выходить sm.gif

Миландр подразумевает любой гос. заказ: летный, наземный, подземный, т.е. всякий, где фигурируют деньги гос-ва.
А в обычной частной коммерции поставят что нибудь более подходящее sm.gif
jcxz
Цитата(VladislavS @ Sep 20 2017, 17:00) *
А зачем вам конкуренты? sm.gif

Да не конкуренты. Хотя-бы сотрудники. А то уже на работу брать некого... sad.gif
Недавно убила фраза друга (далёкого от программирования): "Ну ты же знаешь - самые сильные программисты в Индии, все самые сложные заказы отправляются туда."... И сказано это было в полной уверенности, как само собой разумеющееся. Вот до чего мы докатились уже... sad.gif
inventor
Цитата(VladislavS @ Sep 20 2017, 12:12) *
Значит так. Процессор ваш имеет 128 Кбайт Flash и 48 Кбайт RAM на ядре Cortex-M.
1. Пишете загрузчик, который выполняется из внутренней Flash при старте. В нём инициализируете внешнюю память и копируете в неё из какой-то внешней флэшки основной код полностью.
Затем передаёте управление основной программе
Код
#define APPLICATION_ADDRESS  0xXXXXXXXX
typedef void(*pFunction)(void);
uint32_t app_jump_address = *( uint32_t*) (APPLICATION_ADDRESS + 4);   //извлекаем адрес перехода из вектора Reset
pFunction Jump_To_Application = (pFunction)app_jump_address;                  
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);                   //устанавливаем стек приложения                                          
Jump_To_Application();


2. Основная программа ничего не знает о том что её откуда-то скопировали. Её задача только перенаправить на себя таблицу векторов прерываний.
Код
  #pragma section = ".intvec"
  SCB->VTOR = (uint32_t) __section_begin(".intvec");

А стек я бы расположил всё же во внутренней RAM процессора. 48 кБ для стека это дофига вообще. Да и быстрее эта память обычно.

спасиб, но так я уже делал на STM
тут вот в чем дело - мне хотелось бы чтобы и стек и куча
и все функции изначально поместились бы во внешней RAM
я вот что подумал - в принципе все армы они похожы
и какой нибудь ARM7 не имеет на борту SRAM
но как то может работать сразу из внешней памяти
я вот и подумал что мой простенький контроллер так же сможет

разобрался
в System init написал инициализацию внешней шины
или внешних шин..
далее в скрипте для линкера изменил:

CODE

place in ROM_region { readonly };
place in SRAM_region { block CSTACK };
place in XRAM_region { readwrite, block HEAP };


и все мои переменные оказались во внешней памяти
программа работает
щас попытаюсь функции туда же запихнуть



VladislavS
Цитата(inventor @ Sep 20 2017, 14:02) *
щас попытаюсь функции туда же запихнуть

Где у вас прошивка изначально то размещается?

Цитата(inventor @ Sep 20 2017, 14:02) *
тут вот в чем дело - мне хотелось бы чтобы и стек и куча

А потом вертолёты сами по целям лупят... sad.gif Делать надо как надо, а как не надо делать не надо.
inventor
Цитата(jcxz @ Sep 20 2017, 13:22) *
Недавно убила фраза друга (далёкого от программирования): "Ну ты же знаешь - самые сильные программисты в Индии, все самые сложные заказы отправляются туда."...


вполне возможно так и есть
у индийцев перед китайцами, русскими, французами и прочими зулусами
есть одно преимущество - они англоязычные
то есть они литературу просто читают
а не читают и в голове переводят на свой язык.



Цитата(VladislavS @ Sep 20 2017, 14:07) *
Где у вас прошивка изначально то размещается?

во внутренней flash

Цитата(VladislavS)
А потом вертолёты сами по целям лупят... sad.gif Делать надо как надо, а как не надо делать не надо.

это с запасом, так как сам проект имеет большие объемы данных
с которыми надо работать, а в 48 кБайтах они по любому не поместятся

Forger
Цитата(inventor @ Sep 20 2017, 14:15) *
в 48 кБайтах они по любому не поместятся

Чудовищные глобальные или статически массивы - во внешней ОЗУ, это понятно.
Еще понимаю разместить во внешней ОЗУ кучу (heap).
Но что за проект такой, где прям нужен такой чумовой стек > 48кБ да еще и в относительно медленной памяти?!
VladislavS
Цитата(inventor @ Sep 20 2017, 14:15) *
во внутренней flash

Ну раз он там помещается изначально, что мешает его оттуда выполнять? Зачем его наружу тащить?
inventor
Цитата(Forger @ Sep 20 2017, 14:31) *
Но что за проект такой, где прям нужен такой чумовой стек > 48кБ да еще и в относительно медленной памяти?!

мне он не нужен такого размера,
просто с этой памятью я достаточно намучался
пока она заработала
неочевидная ошибка и плохо определяемая -
не затактировал всего одну ногу
которая была на отличной от других пинов клоке
и нашел ошибку, только когда начал с осциллографом
прозанивать каждую ногу.
ну а как разобрался как ее правильно запускать - просто решил
ее использовать с пользой.
aaarrr
Цитата(inventor @ Sep 20 2017, 15:47) *
просто решил ее использовать с пользой.

Стек в медленной памяти - весьма сомнительная польза. Ему бы вообще в CCM лежать, если она есть.
Obam
Цитата(inventor @ Sep 20 2017, 15:15) *
у индийцев перед китайцами, русскими, французами и прочими зулусами
есть одно преимущество - они англоязычные
то есть они литературу просто читают
а не читают и в голове переводят на свой язык.

Столько лет быть в профессии (TS только здесь с '08) и жалиться на сложность восприятия литературы, написаной на "Катерпиллер Бейсик Инглиш"; не понимаю sad.gif

А за рассказ про ногу и осциллограф - респект (;
Forger
Цитата(inventor @ Sep 20 2017, 15:47) *
мне он не нужен такого размера,
просто с этой памятью я достаточно намучался
пока она заработала
неочевидная ошибка и плохо определяемая -
не затактировал всего одну ногу
которая была на отличной от других пинов клоке
и нашел ошибку, только когда начал с осциллографом
прозанивать каждую ногу.
ну а как разобрался как ее правильно запускать - просто решил
ее использовать с пользой.

Прям текст к песне sm.gif

Цитата
просто с этой памятью я достаточно намучался

smile3046.gif



inventor
ну вобщем разобрался
последний вопрос:
Цитата
Warning[Ta023]: Call to a non __ramfunc function (printf) from within a __ramfunc function C:\proj\mdr32\cpu_clk_test\main.c 74
Warning[Ta022]: Possible rom access (<Constant "%d Mem read error, re...">) from within a __ramfunc function C:\proj\mdr32\cpu_clk_test\main.c 65
Warning[Ta021]: Library call (__aeabi_idivmod) from within a __ramfunc function C:\proj\mdr32\cpu_clk_test\main.c 68


в чем проблемы с этими варнингами?
хелп говорил, что мол то сделано потому

If a function declared _ _ramfunc tries to access ROM, the compiler will issue a warning. This behavior is intended to simplify the creation of upgrade routines, for instance, rewriting parts of flash memory.
If this is not why you have declared the function _ _ramfunc, you can safely ignore or disable these warnings.

то есть насколько это критично?


Цитата(Obam @ Sep 20 2017, 16:09) *
А за рассказ про ногу и осциллограф - респект (;


профессиональный "птичий язык"
со стороны чел услышит покрутит пальцем у виска
VladislavS
Цитата(inventor @ Sep 20 2017, 16:50) *
в чем проблемы с этими варнингами?

Была бы возможность, пару десятков нецензурных варнингов тому что вы делаете я бы вставил.

А варнинги потому что у вас часть кода переехала в RAM, а остальная осталась во Flash и они друг друга вызывают.

Цитата(inventor @ Sep 20 2017, 16:50) *
профессиональный "птичий язык"

Насчёт профессиональный вы себе льстите.
aaarrr
Цитата(inventor @ Sep 20 2017, 16:50) *
то есть насколько это критично?

Вы же код в RAM кладете просто так, а не для исключения доступа к флеш. Так что делайте выводы.
Forger
Цитата
то есть насколько это критично?

В этом проекте это не критично sm.gif

Цитата
профессиональный "птичий язык"
со стороны чел услышит покрутит пальцем у виска

При общении между профессионалами (особенно с незнакомыми) принято использовать человеческий язык, а "птичий язык" - это к "индусам".
inventor
Цитата(aaarrr @ Sep 20 2017, 16:55) *
Вы же код в RAM кладете просто так, а не для исключения доступа к флеш. Так что делайте выводы.

так в чем проблема то?
внешняя память работает медленнее чем flash

Цитата(Forger @ Sep 20 2017, 16:57) *
При общении между профессионалами (особенно с незнакомыми) принято использовать человеческий язык, а "птичий язык" - это к "индусам".


момоему ноги-пины-выводы - это стандартные названия что опят не так?
Forger
Цитата(inventor @ Sep 20 2017, 16:58) *
так в чем проблема то?

Назовите, пожалуйста, изделие, в котом будет крутиться этот "код". Ну, на всякий случай sm.gif

Цитата
момоему ноги-пины-выводы - это стандартные названия что опят не так?

Вот как раз лишь эти слова и были понятны, а все остальное сильно напомнило "текст к песне Земфиры"...
При чем тут некие "пины" и эта внешняя память?
inventor
Цитата(Forger @ Sep 20 2017, 17:06) *
Назовите, пожалуйста, изделие, в котом будет крутиться этот "код". Ну, на всякий случай sm.gif


Вот как раз лишь эти слова и были понятны, а все остальное сильно напомнило "текст к песне Земфиры"...
При чем тут некие "пины" и эта внешняя память?

это нигде не будет крутиться
это просто тест
я плату только развожу
Forger
Цитата(inventor @ Sep 20 2017, 17:26) *
это нигде не будет крутиться
это просто тест

Это еще больше пугает 05.gif

Цитата
я плату только развожу

© "Мопед не мой ...."
lol.gif
jcxz
Цитата(inventor @ Sep 20 2017, 19:47) *
просто с этой памятью я достаточно намучался

...и теперь пора ей отомстить - пусть-ка со стеком помучается biggrin.gif

Цитата(VladislavS @ Sep 20 2017, 20:55) *
Была бы возможность, пару десятков нецензурных варнингов тому что вы делаете я бы вставил.

Работодателю автора можно только посочувствовать biggrin.gif
Forger
Цитата(jcxz @ Sep 20 2017, 19:52) *
Работодателю автора можно только посочувствовать biggrin.gif

Да, видать, уже совсем некого брать: остались лишь те, кто иностранные даташиты читать не умеет, общаются на своем "птичьем языке", предпочитают делают чужую работу вместо своей ..
Печалька, одним словом crying.gif
inventor
Цитата(jcxz @ Sep 20 2017, 19:52) *
Работодателю автора можно только посочувствовать biggrin.gif


я полагаю ему можно лишь позавидовать,
что не могу сказать о вашем
видимо не оценивают такого умного и начитанного в английском
что приходица сидеть во всех топиках "предлагаю работу" wink.gif

Obam
Цитата(inventor @ Sep 20 2017, 17:50) *
профессиональный "птичий язык"
со стороны чел услышит покрутит пальцем у виска

Язык ни при чём; случай - отличная иллюстрация: "свежесобранная схема должна проверяться с соответствующими приборами".
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.