Pull to refresh
47
0
Луговсков Павел Петрович @Archy_Kld

Пользователь

Send message
Это ведь не ассемблер.

Процессорозависимых мест совсем чуть, и они тривиальные по решению.
Тезисно задачи навскидку, без конкретизации конкретного stm:
— инициализация периферии и установка тактирования добавится в stm. Через куб, как заготовку.
— globalTimer на прерывании переполнении счетчика — регистрами будет отличаться, смысл и работа те же.
— Количество тиков для одной секунды и четверти ONE_SECOND и QT_SECOND другое.
— Режимы засыпания устанавливаются по своему, но смысл тот же.
— статей stm blink как грязи, установка значений пинов регистра/потяжки тут в отдельной функции — вся возня в одном месте.
— внешнее прерывание и его поведение в самом глубоком режиме сна, да.
— факт нажатия кнопки — чтение пина на вход.

Поправьте, если не так, но, кажется, ничего не забыл.
Основная логика, как мне видится, не требует изменений под конкретный камень. Сейчас программа компилируется и работает и под ATtiny13 и под ATMega328 (но у меги обработка кнопки отсутствует).

Но, простите, в лоб перетаскивать решение — оно не эффективно по энергопотреблению.
Этот проект вообще проект из серии «упавшее дерево» — пилить несложно, но смысл, возможная цель просто повтора мне непонятны.
Моя цель была выяснить, «а можно ли уместить в тиньку?», и чтобы результат был красивым.
Признаться честно, подготовка статьи была более трудозатратна и отняла больше времени, чем сами светофоры.
Убрать не уберу, естественно: это моя статья, если Вы заметили. Моё право прислушаться к мнению, вежливой просьбе, совету, но не к требованию.
Вы же вольны выразить отношение в комментариях.

Я буду благодарен Вам, и обещаю добавить в статью, сохранив копирайт, Ваш ответ на вопрос: «чем же всё таки вреден перенос глобальной переменной, задействованной только в main() в пространство имен main()?»

Если он будет отличаться от моего мнения: «по расходу RAM ничем, для AVR8 — полезен уменьшением программного кода».
И радостно раздуть себе стек кучей локальных, ага.… и удивиться, что вот как же, опять память куда-то пропала. Трюк не сработал.

Очень надеюсь, что Вы просто увлеклись полемикой, и не считаете так в реальности.
Ну не может же быть, что во входном буфере професионала Вашего уровня остается последнее прочитанное предложение. И что в ответ на утверждение, на формулировку, выделенную жирным, заявлять, что следствием переноса глобальной переменной в локальные в main() может стать нехватка памяти.
Это же ересь.
Судя по минусу на комментарии, в этой оценке я не одинок (это не мой, с моей точки зрения ставить минус собеседнику — признак отсутствия аргументов при наличии эмоций).

Суммарно размер занимаемой и требуемой SRAM при этой рокировке не изменится.
Переменная будет аллоцирована по другому адресу, не снизу, а сверху адресного пространства, но занимать будет ровно столько же памяти.
А вот программный код в 8-битных AVR при этом уменьшится.


Вы и сейчас не готовы отказаться от точки зрения, что размер прошивки не уменьшится при переносе глобальной переменной внутрь main()?

(часть Вашей цитаты про static переменные я удалил, потому что в контексте обсуждения они сущность привнесенная и излишняя. Маловероятно, что кто-либо будет пытаться сохранять в static-ах что-либо между вызовами main())

Вы не пробовали при написании программ использовать не магию и трюки, а какой-то осмысленный подход? Ну там учебники почитать для начала, что ли?

Можно я обосную отсутствие ответа на эти вопросы Вашим заявлением, что что моя личность для Вас вообще не имеет какое-то принципиальное значение?
Если это не так, и позиция изменилась, дайте знать в ответе, я удивлюсь, но расскажу.
Я прикинул — гарантирую, что можно запихать весь код в полкилобайта Flash (а скорее всего меньше), а SRAM, вероятно, вообще не потребуется, кроме стека для прерываний и подпрограмм.

Абсолютно с Вами согласен.
Позволю себе привести цитату себя же из этой же статьи
— Перевести на ассемблер — по ROM ужаться раза в 2 можно, переменные сделать регистровыми — не надо будет постоянно читать-писать в память при работе с ними.
Правда, я тешу себя надеждой, что средний ардуинщик сможет разобраться с приведенным С кодом, а вот с ASM иллюзий особых не испытываю. Статья стала бы сильно менее интересной для многих. Задачка же решенная использованием 95% кристалла, или 50%, или 10% на выходе имеет один и тот же результат. Как Вы наверняка прочитали, не ставилось целью оптимизировать программу по критерию «как можно меньший программный код».
«Ничего, ничего, и без паровозика на пиках неплохо вышло»(с).
Поправьте меня, если я ошибаюсь (но я помню, что все AVR выпускаются с настройкой на внутреннее тактирование 1 МГц.

Необъяснимо. Эта тинька валялась в чулане, при заливке на неё прошивки (видно, что avrdude.exe из пакета MicroCore фьюзы не трогает), в которой я определил #define F_CPU 9600000UL, и по этой частоте считал соответствие переполнения счетчика для глобального таймера, оказалось, что временные интервалы отсчитываются верно. Выяснять отчего она работает так, как ожидал я, а не как должна по настройкам с завода, согласитесь, было бы бессмысленно.

Безусловно, вариант цикла через watchdog по энергопортреблению именно МК (а еще и уход в минимальную частоту, да и Brown-Out Detector отключить полезно) бьёт что угодно. Но как видите, на фоне потребления LED и DC-DC потребление самой тиньки — копейки. Для кратного увеличения времени жизни батарейки лучшее решение, по-моему, через полевые транзисторы вообще отключать батарейку на время «выключения», но это другая схема и другая прошивка.

У Олега корона (замечу, вполне заслуженная, и у меня в закладках несколько его статей с давних пор), отличное понимание теории «как должно работать» и нежелание тратить личное время (абсолютное его право) на вникание «почему так происходит» в конкретном примере собственно и привели к указанной полемике.
Годным итогом которой (для меня как минимум) явилось выяснение аномальных тонкостей работы компилятора, без дискуссии я не стал бы докапываться «а что собственно происходит и почему».
Компилятор avr-gcc (GCC) версии 5.4.0 для МК AVR ATtiny13 и ATMega328 (локальная и глобальная) генерирует различный машинный код при оперировании глобальными и локальными переменными.
На примере инкремента переменной
cnt++;

Для глобального её определения код занимает 10 байт:
...
2e:  80 91 60 00   lds    r24, 0x0060  ; 0x800060 
32:  8f 5f         subi   r24, 0xFF    ; 255
34:  80 93 60 00   sts    0x0060, r24  ; 0x800060 

При определении переменной как локальной 6 байт:
...
2e:    89 81           ldd    r24, Y+1   ; 0x01
30:    8f 5f           subi   r24, 0xFF  ; 255
32:    89 83           std    Y+1, r24   ; 0x01

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

С теоретической точки зрения нет никаких оснований ожидать, что для фактически одного действия — загрузки/выгрузки переменной из ОЗУ в регистры компилятор будет выдавать различный машинный код для локальных и глобальных вариантов использования переменной. Здесь я целиком разделяю мнение Олега.

Реальное же поведение как минимум конкретной версии компилятора, как минимум для двух target МК, которые я проверил — вот тут кроличья нора. Как назвать этот факт — чудо, фокус или баг — зависит лишь от состояния желчного пузыря.
При необходимости сэкономить программную память на AVR, можно вспомнить и попробовать использовать этот трюк. Или сразу при написании программы стараться НЕ использовать глобальные переменные.

Собственно вывод.
Гипотеза, которая высказана в заключительной главе — что можно уменьшить размер прошивки светофора, минимизируя использование глобальных переменных, прошла экспериментальную проверку, подтвердилась, и получила логичное объяснение.
Действительно, моя искренняя благодарность Moduvator и GarryC за участие, пояснения и объяснения.

А теперь результаты эксперимента.
Diff исходника с добавленной бесполезной переменной.

Глобально объявляю uint16_t useless_var __attribute__ ((section (".noinit")));
Размер прошивки 2192 и 1060 байт для компиляции без оптимизации и с оптимизацией по размеру.
В main локально объявляю uint16_t useless_var.
Программа 2146 для компиляции без оптимизации и 982 байт (Ура, влазит (95.9% Full)) с флагом -Os.
Экономия на переносе 78 байт стала позволять поместиться прошивке в ROM тиньки.

Выводы, постараюсь сформулировать аккуратно.
У ATtiny13 (а вероятно у 8-бит AVR вообще) каждая глобальная переменная, которая используется только в основной функции, и может быть перенесена внутрь неё как локальная — вызывает перерасход программной памяти. Объявление хотя бы одной глобальной переменной без директивы расположения в секции ".noinit" так же вызывает перерасход программной памяти. Размер экономии памяти при перемещении переменной в локальную зависит от частоты использования переменной в программе, от количества других переменных, но даже в вырожденном случае, при единственной переменной во всей программе, состоящей из инкремента в цикле для однобайтного типа минимальная экономия будет 4 байта, для двухбайтного типа 10 байт.
Формулировка верна как минимум до двух (Привет, второй кролик) таких переменных.
GarryC намекал, что до 64, почему — мне пока не понятно.
Причина — практически дизассемблированием .elf обнаружил, что avr-gcc\5.4.0-atmel3.6.1 генерит различный код для организации доступа к глобальным и локальным переменным для ATtyny13.
Можно объяснить существующее явление, а не отсутствующее.
Я сейчас, не поверите, ровно так же сидел и проверял.
Что происходит в AVR 8-битном, из-за чего уменьшается потребная программная память мне понятно: глобальная и локальная переменные. Достаточно посмотреть-сравнить размеры памяти на строки 33-34 и дальше.
У глобальных переменных копируется значение из адреса, прямо указываемого в операторе, 4 байта на чтение, 4 байта на запись.
А для чтения локальной — адрес грузится в регистровую пару Y, один раз. А операции чтения и потом записи — двубайтовые.

Олег, оставьте патетику гимназисткам, а диагнозы врачам.
Вопрос несложный, бинарный, зачем вилять, просто ответьте — да или нет?
Ответ на первый вопрос очевиден, история разговора перед Вами: Вы постоянно пытаетесь рассказать мне о чем я думаю, чего хочу и что ощущаю. Мне, признаюсь, несколько неуютно, когда это делает не моя жена.

По второму вопросу.
Давайте оставим в стороне, что фраза, формулировка которой Вам настолько не понравилась, была среди прочих гипотез возможных действий по уменьшению размеров конкретной прошивки, и что Ваша формулировка о количестве требуемой памяти от глобальности переменной не зависит.

Предлагаю эксперимент. В свободное/ближайшее время я в исходник светофора добавлю глобальную переменную. Буду в ней считать что-нибудь, неважно, бесполезное. Количество переключений, или сравнивать с чем то, не суть. Просто новая переменная.
Затем сравню размеры прошивок — где переменная как глобальная, и переменная, та же самая, как локальная. Код выложу на github — чтобы любой мог повторить эксперимент.
Моё мнение — что размеры прошивок будут разными, причем в случае с локальной переменной размер будет меньше.

Вы готовы поддержать противоположную точку зрения, сказать, что по-вашему мнению, размер прошивки не уменьшится?

Всё просто, честно, наглядно.
Да или нет?
Олег, вы полагаете найти какая-то иная причина, вероятно, отличающаяюся от моего описания фокуса с кроликом, прячется где то в этом 70 килобайтном файле?

Я не силён в ассемблерах, могу попросить Вас самого описать — отчего, что было причиной, что в конкретных примерах программ, случилось уменьшение размеров кода при переносе переменной из глобальных в локальные?

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

Вы желаете продолжать спор от того, что вам просто неловко, непривычно, неудобно признать, что Ваши формулировки были неверными вследствие фирменной бескомпромиссности?
Это защитная реакция на что-то, как и попытки рассказа мне, что именно мне представляется, или просто неудачное настроение второй день подряд?
Постановка задачи — посмотреть, что будет, если выпить касторки при кашле, потому что при запоре она помогала?
Поправьте меня, если ошибаюсь, но Вы сейчас компилируете пример под ARM архитектуру (32бита?) для проверки (? нет?) описанного уменьшения размера в программе под 8бит AVR?

Выше, в беседе с Олегом я вставлял картинку сравнения двух дизассемблированных вариантов. Сравнивая ассемблерные файлы здорово видно, что уменьшение размеров кода прошивки (постоянной памяти) произошло из-за конкретных особенностей архитектуры AVR.
— уменьшение кода за счет исчезновения инициализации .bss на 20 байт.
— уменьшение кода за счет того, что длинна инструкции чтения/записи из/в конкретного адреса памяти по конкретному адресу занимает по 4 байта, а длинна команды для чтения из памяти меньше — загрузка Y регистра 2 байта, затем (ldd r24, Y+1) чтение по косвенной адресации размером в 2 байта, запись обратно — тоже только 2 байта.

Чтение/запись во время выполнения программы происходят многократно. Для volatile при любом обращении к переменным, для остальных — перечитывается значение из памяти (4 байт) при первом обращении и при последующих, если между обращениями регистр с переменной был занят другой, записывается в память (4 байт) — каждый раз при изменении.

Но.
Размеры, занимаемые переменными в ОПЕРАТИВНОЙ памяти зависят исключительно от типа переменной. Для глобальных и статических ячейки памяти будут заняты постоянно, а для локальных переменных возможны варианты, причем глобальные переменные будут «внизу» памяти, а локальные на стеке будут «свисать» с потолка, что может привести при нарастании к их пересечению, но это сейчас не важно, как и фрагментация. Адресация же в 32 и 8 битной архитектуре, понятно, фундаментально разная, Игорь использовал более корректный термин «слово» вместо «байта».
Возможен ли для 32битной архитектуры ARM описанный полушутливый трюк — тут смотреть надо, точно так же взять простенький тестовый файл и откомпилировать в разных вариантах, посмотреть размеры прошивки, посмотреть получающийся asm.
Zero-Initialized Data уменьшился на размер массивов, всё верно.
А что, простите, Вы ожидали увидеть?
В первом варианте оперативку зарезервирует компилятор, и добавит еще забой нолями.
Во втором случае оперативка будет выделена после начала работы/исполнения main(). Будет ли далее память инициализироваться, нет ли жизни на Марсе — наука пока данных не получила.

Вот интереснее, что Code уменьшился на 4 байта — но тут я предположить ничего не готов.
Вы чудесны и удивительны. Нет, на самом деле я так вижу.

Вам показывают шляпу, достают оттуда кролика, Вы требуете признать, что кроликов в шляпах нет и быть не может, и клянётесь, что знаете где лежит сотня шляп без кроликов, что доказывает Вашу безусловную правоту.
На Ваших глазах я показываю, как код прошивки уменьшается на 20 байт от переноса глобальной переменной внутрь main() — Вы говорите, что я обязан признать, что всё не так, и так не бывает и быть не может.

Я запускаю руку меж ланит ассистентки и достаю кролика, в большой программе достаю из main() локальную переменную в глобальную, прошивка от этого вырастает на 36 байт — Вы требуете признать, что кролик был не в той норе, о которой могут подумать. Что я просто обязан беспокоиться о травматизме средних ардуинщиков, которые просто обязательно начнут засовывать кроликов.

Нельзя?
Я знаю.
Нельзя делить на ноль, нельзя не защищать GPIO резисторами, нельзя тормозить после входа в поворот на мотоцикле, нельзя допускать срыва машины в занос, нельзя все переменные засовывать в стек, нельзя купаться при температуре -15, нельзя…
Да много чего нельзя.
Возможно, это может показаться немножечко чудом, или ересью, в зависимости от состояния желчного пузыря, но всё это можно, если понимать когда можно и как можно и почему нельзя.

Здесь я уже просто на фактах, на экспериментальных данных, доступных к повторению Вами показал — вот, следите за руками, переносится переменная в локальные, вот asm, вот откуда «появляется» «лишняя» память.
Но нет, Вы мне пытаетесь доказать, что знаете, как можно потратить больше памяти (я про foo-пример со static int), правда, не очень понял цели, у меня в прошивке нет функций со статическими переменными. Но ведь классно, что Вы знаете, что такие бывают.

Нельзя вот так просто взять и отказаться от чуда.
Погладьте кролика, он существует.
Просто гладьте.

Посмотрите исходник — практически все переменные можно исхитриться задействовать вообще без памяти и без флеша, это тоже, кстати, будет чудо. Основных их всего то 9 байт — переписать на ассемблер целиком или вставками — регистров хватит.
Это новая для меня мысль, что время для возни с игрушкой для ребенка (а если быть точным, то даже для двух дочек: для самой старшей, которой интересны будут и статья и нюансы программного кода, и для младшей, которой нравится красочные яркие огоньки на светофоре и интерактив), вот это время — его можно рассматривать как платное.

Ширина проводников 0.3 + зазор 0.3 = 0.6 мм.
Да, Вы правы, действительно не 0.65, получается приврал на лапку семнадцатого воробушка.
Мало чего добавить можно к комментарию. Спасибо разве сказать, хорошо сказано.
Первоначальное заявление Олега было настолько воскитительно бескопромиссным и безапелляционным, что было сложно удержаться и не показать, что жесткие формулировки обязаны быть еще и всеохватывающими. Заявлять, что снег всегда только белый — дискредетировать свою же информацию, хотя в подавляющей массе своей действительно не желтый. Переменная С в памяти требует не только место для себя, любимой, но существуют и накладные расходы, в стесненных рамках надо понимать и помнить, что они есть и какие именно. Чтобы была возможность выбирать чем платить будем — лишним словом ROM при обращении или лишним байтом оперативки или регистром. Я не просто так у каждой глобальной переменной писал в комментарии потребный размер, это ценники. Тинька с её 64 байтами RAM и 1к ROM отлично иллюстрирует необходимостью ощущать узкие стенки коридора решений.
Мне казалось очевидным, что фраза по которой начался спор, наряду со своими соседками в перечислении, является одной из спекулятивных гипотез, требующих проверки и (возможно) обеспечивающую экономию программной памяти. В случае, если это потребуется, и какой случай так же описан.
Но если я в цирке смеюсь, это не значит, что все такие же, и так же воспримут очевидные мне вещи.
Смешно тут то, что в конкретном программном коде оценка навскидку оказалось верной. А верное в базе мнение оппонента из-за своей неполноты и жесткости конструкции определения наоборот было опровергнуто экспериментом.
Но что еще ожидать от циркового представления?
Судите сами: стоимость ATtiny13 = 0 (ноль) рублей, я там выше писал, мне её жаба выдала со склада. Любая положительная цена, хоть одна копейка за десяток или сотню stm — это в миллиарды, в триллионы раз большая сумма.
А если серьёзно, нужно смотреть по первоначальным требованиям. При необходимости разработки тиражируемого устройства, и DC-DC лучше свой на плате расположить, и МК я выбирал бы другой. И сравнивал бы наверное сначала по критерию энергопотребления различные варианты, по удобству заливки прошивки на потоке и обновления, по фаршу, а затем уже по цене. Возможно, что что-то из TI MSP430 оказалось бы интереснее STM32. Но в любом МК, какого бы размера не были ROM и RAM всегда может случится ситуация нехватки десятка байт. Или пары ножек. Ресурсы — они конечны по определению, хорошо, когда понимаешь какиеконкретно есть варианты более рационального использования.

Проблемы с трассировкой решать меня научили еще на DOSовском PCADе когда-то. И изготавление ПП по ЛУТ для FT232RL с её шириной проводников и зазорами АФАИР 0.3мм тоже пройденный этап. Да и сейчас подготовка гербера и сдача на производство услуга очень распространенная.
Искренне сожалею, что вы в статье увидели использование Ардуино для уменьшения потребления с 3мА до 1мА.
На самом деле мне хотелось написать другие цифры, на порядок большие, и рассказать в каких ситуациях они возникают. Хотелось рассказать, что можно программировать без Ардуино и какие плюсы при этом пожинать. Мечталось показать порядок влияния на энергопотребление всего устройства разных его частей, акцентировать, что потребление самого МК может стать меньше погрешности ошибок измерения. Показать, что из практически веток, травы и лесного ветра можно сделать забавную игрушку, причем значительная часть работы будет вместе со своими детьми.

Извините, что у меня не получилось всё это показать и сделать для Вас.
Я, честное слово, старался.
Интригующе смотрится Ваша осведомленность в том, что и как мне представляется. И желание оставить вне поля зрения факт, что переменные не только читаются, но и пишутся в память при изменении — те же +2 байта.
Досадно, что Вы сейчас приводите свою экспертную оценку, споря с результатами работы компилятора на конкретном примере.
Но, может, на «большом» исходнике TrafficLight13.ino всё будет по отстаиваемой Вами точке зрения?
Напомню её "Глобальная переменная в C занимает ровно столько байт, сколько положено размерностью её типа."
Там есть подходящая локальная переменная, перенесем ее в глобальные для проверки.
int main() {
	uint8_t	current_signal;				// 1 байт на текущее состояние, номер в traffic_signals

При компиляции с нулевой оптимизацией -О0 размер прошивки 2038 bytes (199.0% Full). С параметром -Os 938 bytes (91.6% Full).
Если переменную вынести в глобальные размер прошивки 2066 bytes (201.8% Full) с флагом О0, а с оптимизацией по размеру 974 bytes (95.1% Full).
И если описать её как глобальную в секции .noinit размер прошивки те же
2066 bytes (201.8% Full) (что логично — остальные глобальные в .noinit не попали, 20 байт на его обнуление все равно задействованы) и с оптимизацией по размеру -Оs те же 974 bytes (95.1% Full).
Компиляция без оптимизации интересна тем, что asm файл довольно точно следует исходному коду на С, никакой отсебятины и экономии от компилятора не добавляется.
Однако всё равно 2046 больше, чем 2038, а разница при оптимизации по размеру 974 и 938 еще более впечатляющая — на фоне суммарного размера ROM.
Увы.
Буду краток.
— Перенос глобальной переменной в локальную дает экспериментально точно выявленную экономию: 36 байт. Ранее, напомню, моя оценка такого действия была «примерно добавит 50 байт». Если все глобальные вынести в .noinit, вероятно, экономия будет +20. Суммарно на все, но среди остальных несколько двухбайтовых типов, где экономия обещает быть больше, то на то.
— Ваше мнение о том, что место в памяти для глобальной переменной расходуется только на размер типа не подвердилось, упущены расходы на инициализацию .bss и повышенные расходы на чтение и запись, извините.
— Вы не путаете виды памяти, обещая мне показать перерасход ОЗУ в контексте беседы о экономии ROM?
— Самое забавное, Вы, ошибаясь конкретно в этом примере, по общей теории практически во многом правы. Но правы «вообще», не по обсуждаемой конкретной задаче минимизации конкретного кода в конкретном килобайтном кристалле.
Простите великодушно, но заменив формулировку на "накладные расходы на обнуление .bss будут распределены на все переменные", или так же "при суммарном размере переменных более 20 байт, накладные расходы на каждую переменную на обнуление .bss станут меньше размера переменной" Вы получите гораздо более убедительную позицию в споре.
Правда при этом Вы вынуждены будете расстаться с безапелляционностью первичных суждений.
Вариант отличный, от инициализации .bss избавляет.
Но дальше всё равно по расходу памяти уступит локальным переменным.
Тот же микрокод, где счетчик как
uint8_t cnt __attribute__ ((section (".noinit")));

Компиляция с -О0, локальная переменная прошивка 58 байт, глобальная в секции .noinit — 62 байта.
Слева — вариант с глобальной переменной в .noinit, справа — локальное объявление, остальные части листинга практически одинаковы.
Обратите внимание: по смещению 2e (начало while(1) слева) чтение значения из адреса — это 4 байта, инкремент (ага, вычесть 255), затем опять 4 байта на сохранение значения.
image
Чем больше раз мы будем обращаться к переменной, тем больше вырастет код.
Для переменных с типами размером больше байта ситуация еще грустнее.

Кстати, наверное, стоило поделиться хитростью в статье.
Чтобы по-быстрому откомпилировать-залить, или посмотреть асм-листинг не запуская IDE, я открываю cpp/c/ino в notepad++, и прямо из него по F5 (или меню Запуск-Запуск) запускаю коммандный файл из директории ./gcc.
"....path/gcc/0_MAKE & asm-O0.cmd" $(FULL_CURRENT_PATH)
Добавил «0_MAKE & asm-O0.cmd» в репозиторий на github.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Date of birth
Registered
Activity