Перейти к содержанию
    

Vivado BD как спрятать параметризируемые порты AXI

Доброго времени суток!

Для облегчения жизни при сборке модулей в готовую систему решил сделать свой модуль как отдельное IP ядро с всеми необходимыми настройками параметров, включая размножение внутренних компонентов обработки, которые состоят из входного сигнала, логики+БРАМ, и выходной порт AXI. Всё написано на SystemVerilog и "выходной" порт AXI запихнут в отдельный интерфейс.

Правдами и неправдами сумел собрать IP и договорится с bd что это и вправду компонент с AXI шинами (пришлось перегенерировать кучу портов и создать массу ненужных параметров, те которые с ID и USER). Для удобства создал параметр, который говорит сколько внутри размножено модулей и соответственно сколько входных сигналов и выодных AXIшин должно быть прокинуто. С помощью настройки Interface presence указал когда должны появляться порты (коих 32 штуки) по условию ($P_COMPONENT_NUM >N) для входных сигналов и AXI. Входные порты конфигурируются нормально без каких бы то ни было вопросов, все неиспользующиеся подвешиваются на 0. Но вот AXI начинает творить чудеса. BDувидел, что это AXI и что у него сконфигурированы все порты в соответствии с проприетарным интерфейсом "aximm", да вот только он пытается подключить все 32 AXI порта к несуществующим сигналам.

В коде интерфейс сгенерированного топ файла IPядра выглядит где-то так:

Spoiler

 


module top_component
	i_clk,
	s_axi_aclk,
	s_axi_aresetn,

	s00_axi_0_s00_axi_araddr,
	s00_axi_0_s00_axi_arprot,
	s00_axi_0_s00_axi_arready,
	...					// остальные порты AXI Lite интерфейса. Тут проблем нет
    s_axi_0_s_axi_awaddr,
    s_axi_0_s_axi_awlen,
    s_axi_0_s_axi_awsize,
	...					// остальные порты AXI MM интерфейса 0.
    s_axi_1_s_axi_awaddr,
    s_axi_1_s_axi_awlen,
    s_axi_1_s_axi_awsize,
	...					// остальные порты AXI MM интерфейса 1. модуль сконфигурирован на 2 порта
	i_signal_0,
	i_signal_1);
	
	// Далее идёт автоматическая конфигурация объявленных портов
	input wire i_clk;
	input s_axi_aclk;
	input s_axi_aresetn;

	// конфигурацию X_INTERFACE_INFO я опустил
    inout wire [6 : 0] s00_axi_0_s00_axi_araddr;
    inout wire [2 : 0] s00_axi_0_s00_axi_arprot;
    inout wire s00_axi_0_s00_axi_arready;
	...
    inout wire [11 : 0] s_axi_0_s_axi_awaddr;
    inout wire [7 : 0] s_axi_0_s_axi_awlen;
    inout wire [2 : 0] s_axi_0_s_axi_awsize;
	...
    inout wire [11 : 0] s_axi_1_s_axi_awaddr;
    inout wire [7 : 0] s_axi_1_s_axi_awlen;
    inout wire [2 : 0] s_axi_1_s_axi_awsize;
	...
	input wire i_signal_0;
	i_signal_0 i_signal_1;

Тут ничего неординароного, а вот дальше генератор пытается создать и подключить все AXI порты:

aximm S_AXI_0();							// Порт 0 присутствует
assign S_AXI_0.WLAST = s_axi_0_s_axi_wlast;
assign S_AXI_0.BREADY = s_axi_0_s_axi_bready;
assign S_AXI_0.AWLEN = s_axi_0_s_axi_awlen;
...
aximm S_AXI_1();							// Порт 1 присутствует
assign S_AXI_1.WLAST = s_axi_1_s_axi_wlast;
assign S_AXI_1.BREADY = s_axi_1_s_axi_bready;
assign S_AXI_1.AWLEN = s_axi_1_s_axi_awlen;
...
aximm S_AXI_2();							// Порт 2 отсутствует (не сконфигурирован)
assign S_AXI_2.WLAST = s_axi_2_s_axi_wlast;
assign S_AXI_2.BREADY = s_axi_2_s_axi_bready;
assign S_AXI_2.AWLEN = s_axi_2_s_axi_awlen;
...
aximm S_AXI_3();							// Порт 3 отсутствует (не сконфигурирован)
assign S_AXI_3.WLAST = s_axi_3_s_axi_wlast;
assign S_AXI_3.BREADY = s_axi_3_s_axi_bready;
assign S_AXI_3.AWLEN = s_axi_3_s_axi_awlen;
...
// И так все 32 порта из которых реально входит только 2 первых

Ну и дальше идёт подключение к моему сгенерированному IP ядру.

top_component inst (
    .i_clk(i_clk),
    .s_axi_aclk(s_axi_aclk),
    .s_axi_aresetn(s_axi_aresetn),
    .s00_axi_0_s00_axi_araddr(s00_axi_0_s00_axi_araddr),
    .s00_axi_0_s00_axi_arprot(s00_axi_0_s00_axi_arprot),
    .s00_axi_0_s00_axi_arready(s00_axi_0_s00_axi_arready),
  	...						// Конфигурация AXI Lite. Здесь всё нормально
    .S_AXI_0(S_AXI_0),		// Подключён существующий порт
    .S_AXI_1(S_AXI_1),		// Подключён существующий порт
    .S_AXI_2(S_AXI_2),		// Подключён несуществующий порт. Который просто объявлен в топе
    .S_AXI_3(S_AXI_3),		// Подключён несуществующий порт. Который просто объявлен в топе
  	...						// И так далее до 31-го порта
    .i_signal_0(i_signal_0),// Подключён существующий вход
    .i_signal_1(i_signal_0),// Подключён существующий вход
    .i_signal_2(1'B0),		// Подключён несуществующий вход. Который просто объявлен в топе
    .i_signal_3(1'B0),		// Подключён несуществующий вход. Который просто объявлен в топе
  	...						// И так далее до 31-го порта
    );

Как избежать такой ситуации? Нужно дополнительно указать какие-то параметры или вручную создать порты AXI?

Этот файл генерируется автоматические и Xilinxпредупреждает:

// DO NOT MODIFY THIS FILE.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

Ничего не понятно - кто что генерирует? 

Я так понял что top_component это враппер вашего IP котрый для вас визард сгенерировал и вы его трогать боитесь?  Не бойтесь,  можете править  как хотите  или использовать как образец для создания своего враппера для компонента. В котором уже сами  управляйте назначением/заглушкой портов в зависимости от параметра.  

Удачи! Rob.

Изменено пользователем RobFPGA

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 minutes ago, RobFPGA said:

Ничего не понятно - кто что генерирует? 

Собственно да, тут не очевидно.

Весь приведённый код - это автоматически сгенерированный враппер в BLOCK DESIGN интерфейсе. собственно то что подключается в конце - это и есть мною написанное IP ядро. Со всеми установками и параметрами (то которое подключается top_component inst).

14 minutes ago, RobFPGA said:

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

Собственно говоря я то не боюсь. Но проблема в том, что когда я буду перегенерировать BD (команда Generate Block Design) оно всё перезатрёт и опять по-новой. Может есть какой-то нюанс неучтённый, чтобы оно не трогало мой правленный враппер...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

1 hour ago, Nick_K said:

Но проблема в том, что когда я буду перегенерировать BD (команда Generate Block Design) оно всё перезатрёт и опять по-новой.

С какого это перепугу?  Этот враппер генерируется когда вы пакуете в автозак  в репозиторий ваш IP. Если не хотите такого халявного беспредела сделайте свой топ врапер,  например типа такого:

Spoiler

// AXI4_macro.vh
`define AXI4_PORT_SLAVE(pname,wha,whd,whid,whreg) \
input  wire                 pname``_aclk     , \
input  wire                 pname``_arstn    , \
input  wire [    wha``-1:0] pname``_awaddr   , \
...

`include "AXI4_macro.vh"

module my_axi_core_top #(parameter
  AXI_NUM    = 1,
  AXI_ADDR_WH=16,
  AXI_DATA_WH=32
)(
  `AXI4_PORT_SLAVE(s00, AXI_ADDR_WH, AXI_DATA_WH, 0, 0),
  // ...
  `AXI4_PORT_SLAVE(s15, AXI_ADDR_WH, AXI_DATA_WH, 0, 0)
);

`define AXI4_SLAVE_IF_MAP(sel, pname, ifname) \
  if ((sel)) begin : g_map_``pname            \
    assign ifname``.awaddr = pname``_awaddr;  \
    ...
    assign pname``_awready = ifname``.awready;\
  end                                         \
  else begin : g_skip_``pname                 \
    assign pname``_awready = 1'b1;            \
    ...
    assign pname``_rdata   = 'x;              \
  end

if_axi4 #(AXI_ADDR_WH, AXI_DATA_WH) s_axi_if [AXI_NUM] ();

generate
  `AXI4_SLAVE_IF_MAP(AXI_NUM>0, s00, s_axi_if[0])
  ...
  `AXI4_SLAVE_IF_MAP(AXI_NUM>15, s15, s_axi_if[15])
endgenerate
....  
my_axi_modue #(
  .AXI_NUM(AXI_NUM), 
...

 

Такой топ пакуется без всяких проблем  c автоматическим распознаванием AXI. Останется только добавить параметры

set_property enablement_dependency {$AXI_NUM>0}  [ipx::get_bus_interfaces s00       -of_objects [ipx::current_core]]
set_property enablement_dependency {$AXI_NUM>0}  [ipx::get_bus_interfaces s00_aclk  -of_objects [ipx::current_core]]
set_property enablement_dependency {$AXI_NUM>0}  [ipx::get_bus_interfaces s00_arstn -of_objects [ipx::current_core]]
...
set_property enablement_dependency {$AXI_NUM>15} [ipx::get_bus_interfaces s15       -of_objects [ipx::current_core]]
set_property enablement_dependency {$AXI_NUM>15} [ipx::get_bus_interfaces s15_aclk  -of_objects [ipx::current_core]]
set_property enablement_dependency {$AXI_NUM>15} [ipx::get_bus_interfaces s15_arstn -of_objects [ipx::current_core]]

 Удачи! Rob.

Изменено пользователем RobFPGA

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@RobFPGA

Спасибо большое за пример. Наконец-то дошли руки до модуля. Я так понял, что Вы предлагаете просто всё объявить и потом только подключать нужные порты. Только у меня возникает вопрос где использовать эту часть:

On 8/15/2019 at 5:25 PM, RobFPGA said:

set_property enablement_dependency {$AXI_NUM>0} [ipx::get_bus_interfaces s00 -of_objects [ipx::current_core]] set_property enablement_dependency {$AXI_NUM>0} [ipx::get_bus_interfaces s00_aclk -of_objects [ipx::current_core]] set_property enablement_dependency {$AXI_NUM>0} [ipx::get_bus_interfaces s00_arstn -of_objects [ipx::current_core]] ... set_property enablement_dependency {$AXI_NUM>15} [ipx::get_bus_interfaces s15 -of_objects [ipx::current_core]] set_property enablement_dependency {$AXI_NUM>15} [ipx::get_bus_interfaces s15_aclk -of_objects [ipx::current_core]] set_property enablement_dependency {$AXI_NUM>15} [ipx::get_bus_interfaces s15_arstn -of_objects [ipx::current_core]]

похоже на tcl скрипт, но я так понимаю, что его нужно активировать автоматически каждый раз, когда создаётся примитив (короче говоря где это вписать, чтобы Vivado видела эти проперти?)

Или хватит одноразового прохода скрипта при создании IP Core?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

8 minutes ago, Nick_K said:

Я так понял, что Вы предлагаете просто всё объявить и потом только подключать нужные порты

Это не я предлагаю - увы в RTL нельзя динамически объявлять порты. Поэтому если вы планируете создавать модуль с переменным числом используемых портов то их максимальное количество в любом случае надо заранее объявлять явно.  

3 minutes ago, Nick_K said:

set_property enablement_dependency {$AXI_NUM>0} ...

Это кусок скрипта для задания параметров IP корки при создании оной в IP интеграторе. Огромный плюс консоли Vivado в том что в логе видно почти вся мышиная возня на формах.  Один раз поклацал - сохранил в скрипт и потом повторяй все автоматом.  

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, RobFPGA said:

Это кусок скрипта для задания параметров IP корки при создании оной в IP интеграторе. Огромный плюс консоли Vivado в том что в логе видно почти вся мышиная возня на формах.  Один раз поклацал - сохранил в скрипт и потом повторяй все автоматом.

Это я заметил и уже давно использую) Просто не сразу сообразил куда вкинуть нужно былоданный скрипт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/15/2019 at 5:25 PM, RobFPGA said:

if_axi4 #(AXI_ADDR_WH, AXI_DATA_WH) s_axi_if [AXI_NUM] ();

Интересный момент. Вы объявляете массив интерфейсов в сигнал. Насколько я видел Vivado не поддерживает массивы интерфейсов (к примеру тут). Да и сам вивадо ругается на массив интерфейсов. Но меня больше интересует момент подключения в:

On 8/15/2019 at 5:25 PM, RobFPGA said:

my_axi_modue #(

.AXI_NUM(AXI_NUM),

...

Вы подключаете в массив или там отдельные порты для каждого AXI?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

1 hour ago, Nick_K said:

Насколько я видел Vivado не поддерживает массивы интерфейсов

Да вроде как синтезируется такое без проблем (во всяком случае в 18.3 и 19.1). Вот для примера 

Spoiler

interface if_test #(DATA_WH=4) (input wire clk, rst);
  logic               ena ;
  logic [DATA_WH-1:0] data;
endinterface

(* keep_hierarchy="yes" *)
module prc_cnt #(WH=4) (
  if_test cnt_if[WH]
);

generate
  for (genvar gi=0; gi<WH; ++gi) begin : g_cnt
    always_ff @(posedge cnt_if[gi].clk) begin
      if (cnt_if[gi].rst) begin
        cnt_if[gi].data <= '0;
      end
      else if (cnt_if[gi].ena) begin
        cnt_if[gi].data <= cnt_if[gi].data + 1'b1;
      end
    end
  end
endgenerate
endmodule

module test_if1 #(parameter
  CNT_NUM = 3,
  DATA_WH = 4
) (
  input  wire                             clk,  
  input  wire                             rst,  
  input  wire  [CNT_NUM-1:0]              din,
  output logic [CNT_NUM-1:0][DATA_WH-1:0] dou
);

(* keep="true" *)
if_test #(DATA_WH) count_if [CNT_NUM] (clk, rst);

generate
  for (genvar gi=0; gi<CNT_NUM; ++gi) begin : g_io
    assign count_if[gi].ena=din[gi];
    assign dou[gi] = count_if[gi].data;
  end
endgenerate

prc_cnt #(CNT_NUM) (count_if);

endmodule

 

 

1 hour ago, Nick_K said:

Вы подключаете в массив или там отдельные порты для каждого AXI?

Это как вам удобно будет. Можно массивом интерфейсов, можно интерфейсом массивов, а можно и  плоскими шинами. Это уже вам решать. Для этого этот враппер и нужен. 

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 29.08.2019 в 21:33, RobFPGA сказал:

Да вроде как синтезируется такое без проблем (во всяком случае в 18.3 и 19.1).

К сожалению, тут не всё так хорошо, как хотелось бы. SystemVerilog вообще язык достаточно "одиозный" (видимо потому, что нишевый) - его придумывает, проектирует и реализует группа энтузиастов, работающих преимущественно в "большой тройке" (Synopsis, Cadence, Mentor), они привносят решения, некоторые из которых оказываются непродуманными, что вызывает потом проблемы, приходится от части этих решений отказываться. Вся эта кипучая "жизнь" видна на паре-тройки их профильных форумов (типа verificationacademy.com/forums и подобных).

 

Вот реальный пример. Использовали много лет в проектах такой подход. Всё замечательно - задал параметром число модпортов и вуаля. Но вот буквально с месяц-другой назад наталкиваемся на неприятность - квеста 10.7с даёт отлуп коду, который прекрасно работает в 10.4с (и более ранних). Синтез тоже не ругается (Вивадо 2018). Думаем - ну, косяк в новой квесте. Стали копать тему и нашли. Оказывается, теперь эта конструкция - генерирование модпортов как по ссылке запрещено. Полувнятные объяснения нашли на одном из форумов, где дяденька (очевидно, один из авторов (из Accelera)) про это прямо написал, что, де, мы были молодыми, смелыми и отважными, и принимали решения, не все из которых оказались способными выдержать проверку жизнью, и генерируемые модпорты из этой серии. По технике там претензии к этому решению состоят в том, что конструкция generate вводит новый уровень иерархии (ну да, так и есть - там у цикла for надо заводить именованный блок), и модпорт оказывается как бы ниже уровнем иерархии по отношению к своему же интерфейсу, а это идеологически неверно: модпорт - это не иерархическая сущность, а просто языковое средство указания направления сигналов.

 

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

 

Массив интерфейсов тоже мутная тема. С их синтезом всегда были проблемы - одни тулы поддерживают, другие - нет. Попытка разобраться в вопросе приводит к следующему утверждению (тоже от кого-то из гур Accelera (не помню уже, на форуме или в блоге прочитал), излагаю своими словами): массив интерфейсов - идеологически неверная сущность, т.к. массив всегда предполагает коллекцию гомогенных объектов, которые все одинаковые. Но интерфейсы не таковы - ведь там каждому экземпляру можно, например, задать параметр, который что-то внутри поменяет, поэтому экземпляры интерфейса в массиве будут не одинаковыми, и код, который работает с этим массивом может работать с ними неверно. В общем, тут возникает поле для трудноуловимых ошибок. По этой же причине, кстати нельзя создать массив модулей.

 

В общем, массив интерфейсов - вещь, непригодная для промышленного программирования (моё мнение). Из-за причины, приведённой выше. И из-за того (а это, полагаю, следствие этой причины), что синтез то ли поддерживает, то ли нет.  И что же остаётся "бедному крестьянину" на ниве плисоводства для параметризации интерконнектов? Я пока не вижу ничего, кроме массивов структур. Да, придётся метать пару структур - на вход и на выход (если без inout). Но зато хоть какая-то параметризация.

 

* * *

Если пофантазировать на тему "а как бы можно было это сделать?", то приходит в голову простая, в общем-то, идея - просто расширить средства языка до нормальной поддержки параметризации модпортов - не с помощью generate, которая уже имеет свою устоявшуюся семантику, а какое-то новое средство со своим ключевым словом - да хоть generate_modport, которая не вводит новый уровень иерархии, а просто позволяет объявлять модпорты в цикле, и всё.

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

37 minutes ago, dxp said:

 Да, придётся метать пару структур - на вход и на выход (если без inout). Но зато хоть какая-то параметризация. 

и помнить про compilation unit. квеста обожает ткнуть носом в несовместимость структур, если одна и та же структура, в портах разных модулей, была скомплирована по отдельности)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@dxp Какой у Вас интересный текст получился. Я как человек "новый" в SVне совсем понимаю о чём речь, но после 8 летней писанины на VHDL очень обрадовался понятию интерфейсов с одной стороны. С другой же, что мешает просто написать модуль, в котором происходит мешанина как в интерфейсе? Да он будет неудобно подключаться, но по сути это тот же интерфейс. за то с генерациями, блек-джеком и :curtsey:.

Ну или оставить как есть в VHDL: структура в пакете и развлекайся. Вполне сносно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Приветствую!

11 hours ago, dxp said:

Массив интерфейсов тоже мутная тема. С их синтезом всегда были проблемы - одни тулы поддерживают, другие - нет. Попытка разобраться в вопросе приводит к следующему утверждению (тоже от кого-то из гур Accelera (не помню уже, на форуме или в блоге прочитал), излагаю своими словами): массив интерфейсов - идеологически неверная сущность, т.к. массив всегда предполагает коллекцию гомогенных объектов, которые все одинаковые. Но интерфейсы не таковы - ведь там каждому экземпляру можно, например, задать параметр, который что-то внутри поменяет, поэтому экземпляры интерфейса в массиве будут не одинаковыми, и код, который работает с этим массивом может работать с ними неверно.

Интересно как можно объявить массив неодинаковых интерфейсов? Или вы имели в виду на объявлять  разных интерфейсов (одного типа, но с разными параметрами) а потом как  то свести их в один массив? 

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

Удачи! Rob.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, des00 сказал:

и помнить про compilation unit. квеста обожает ткнуть носом в несовместимость структур, если одна и та же структура, в портах разных модулей, была скомплирована по отдельности)

А как это технически происходит? Компиляция-то, вроде, всех модулей проекта происходит за один проход.

2 часа назад, Nick_K сказал:

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

Неудобство некоторое - полбеды. Но только что это даёт? Как вы будете у модуля параметрически задавать количество портов? Тем же массивом структур, надо полагать. Ну, а если так, то как-то не видно смысла в такой промежуточной сущности - можно ведь эти же массивы структур (в качестве портов) сразу по целевым модулям раскидать.

1 час назад, RobFPGA сказал:

Интересно как можно объявить массив неодинаковых интересов? Или вы имели в виду на объявлять  разных интерфейсов (одного типа, но с разными параметрами) а потом как  то свести их в один массив? 

Типа того. Ведь если просто тупо их объявить, то отваливается возможность параметризации и они по сути вырождаются в "улучшенные структуры". По-хорошему, просится возможность создавать интерфейсы с помощью generate for, но иметь возможность при этом как-то их объединять в коллекцию, по которой можно было бы итерировать - для автоматизации описания. Технически ведь тут принципиальных трудностей нет - просто средства автоматической генерации и автоматизации кода. Каким-нибудь сторонним скриптом на питоне или даже тикле это можно делать уже и сейчас.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 минут назад, dxp сказал:

Компиляция-то, вроде, всех модулей проекта происходит за один проход.

Почему это? Все модули компилируются независимо друг от друга.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...