Skocz do zawartości
rafal2808

Project A - czyli kolejny procesor w FPGA

Pomocna odpowiedź

Dostępna jest już kolejna wersja układu wyposażona w pełni funkcjonalny interfejs UART. Poza tym dodane zostały nowe rozkazy dla ALU oraz sam kod procesora został zaktualizowany. Na moim blogu można dowiedzieć się więcej na temat budowy układu oraz pobrać projekt.

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Kolejna wersja procesora jest już gotowa. Tym razem dodana została obsługa rozkazu powrotu. Dzięki temu nie musimy znać dokładnego miejsca, w którym przerwany został program główny. Od teraz procesor przechowuje adres w odpowiednim rejestrze. Więcej na temat mojego projektu można przeczytać tutaj.

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

133961285_architektura(2).thumb.jpg.2753a93642f8e0f43bf332650d830546.jpg

 

Zapraszam do zapoznania się z zaktualizowaną wersją opisu procesora Q818. Pojawiła się już dość spora sekcja poświęcona architekturze układu oraz blokom tworzącym procesor.

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

@rafal2808 super, że projekt się rozwija 🙂 Jakie dalsze plany?

Udostępnij ten post


Link to post
Share on other sites

@Treker W przyszłości na pewno pojawi się obsługa kolejnych interfejsów takich jak SPI, I2C itd. Dodatkowo planuję też wprowadzenie obsługi wyświetlacza LCD oraz 7-segmentowego. Poważnie też myślę nad dodaniem zewnętrznej pamięci RAM. Można by wówczas zrezygnować z implementowania jej na strukturze FPGA i zwiększyć pojemność pamięci ROM, ale to temat na dalszą przyszłość. Oczywiście cały czas staram się pracować nad projektem i metodą małych kroków zwiększać jego funkcjonalność.  

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Porządki w projekcie, ciąg dalszy😀

Można powiedzieć że, zaczynam od początku. Projekt zyskał całkowicie nową architekturę, jest to ulepszona wersja pierwotnego pomysłu. Najważniejszym elementem jest dodanie obsługi przerwań zewnętrznych jak i wewnętrznych. Do tej pory żaden realizowany przeze mnie projekt procesora nie posiadał tej funkcji.

Sama forma pracy nad projektem również ulegnie pewnej zmianie. Z czasem pojawiać się będą wpisy (facebook+blog+forbot) informujące o zakończeniu etapu projektowania poszczególnych elementów. Dotychczas informowałem raczej o ukończeniu, czy zaktualizowaniu całego projektu, nie jego konkretnych elementów.

Dodatkowo nazwa procesora również ulega zmianie od teraz będzie to "Project A".

architektura.thumb.jpg.68adedb8ccad28aebce95ae8af51bc8c.jpg

Tak prezentuje się architektura procesora. Każdy z bloków możemy przypisać do jednej z czterech kategorii. Kolorem żółtym zaznaczone zostały wszystkie rejestry, czerwonym bloki pamięci (RAM, ROM), niebieskim multipleksery. Na zielono zaznaczone zostały bloki, które możemy określić jako te najbardziej zaawansowane takie jak ALU, dekoder instrukcji czy kontroler wyświetlaczy 7 segmentowych. Blok kryjący się pod tajemniczą nazwą "LOGIC" jest najprościej mówiąc głównym elementem układu sterowania. To tutaj na podstawie instrukcji, zegara oraz kilku pomniejszych sygnałów określających stan procesora generowane są wszystkie sygnały sterujące dla innych bloków. 

Najważniejszą z nowości jest dodanie układu przerwań. Na początek obsługiwane będą trzy typy przerwań. Dwa z nich będą aktywowane w momencie wysłania lub odbioru znaku poprzez interfejs UART, trzecim natomiast będzie sygnał zewnętrzny (np. przycisk). Oczywiście dzięki rejestrowi ICR (interrupt control register) możliwe będzie wyłączenie dowolnego z przerwań. 

Fizycznie projekt zrealizowany zostanie na dobrze już znanej płytce Elbert v2. Oczywiście założenie iż wszystko zrealizowane ma być tylko na tej płytce pozostaje nadal aktualne.

Ciąg dalszy nastąpi...😉

Jeśli ktoś zainteresowany jest tym czy innym z moich projektów zapraszam na prowadzoną przeze mnie stronę na facebooku, gdyż to właśnie tam w pierwszej kolejności pojawiają się najnowsze informację. 

Edytowano przez rafal2808
  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Ma kilka pytań:

Przede wszystkim dziwne jest ścisłe powiązanie (a raczej brak wyraźnego rozgraniczenia) procesora i peryferiów. Mam nadzieję, że nie zrobisz osobnych instrukcji do komunikacji np. z rejestrami UARTa czy LED a będzie to jakoś ujednolicone, np. przez użycie osobnej przestrzeni adresowej banku rejestrów I/O. Z punktu widzenia struktury projektu chyba wygodniej byłoby wydzielić duże bloki właśnie typu procesor, peryferia (UART, LED, ew. kontroler przerwań, może w przyszłości jakieś timery itp) czy pamięć choćby po to, by po sprzęgnięciu je jakimiś dobrze określonymi magistralami można było w łatwy sposób dodawać/usuwać bloki. Obecnie to wygląda na słabo modyfikowalne.

Dlaczego aby zapisać coś do B trzeba przepuścić to przez A? Wydaje się to dziwne. Nie możesz przez to używać ALU do innych rzeczy np. inkrementacji/dekrementacji B czy ew. innych przyszłych rejestrów.

Czy jest to procesor 4-, 8-, czy 16-bitowy?

Jak szeroką przewidujesz pamięć programu (albo jak długi kod operacyjny instrukcji)?

Czy będziesz skupiał się na szybkości wykonywania instrukcji (a więc rozbudowie logiki) czy raczej na zajętości FPGA (czyli raczej układ mikroprogramowany)?

Wydaje się, że system przerwań powinien być ściśle  powiązany z głównym sekwencerem a tego tu nie widać.

Czy przyjęte przerwanie będzie jakoś sprzętowo zachowywać kontekst? Bez możliwości zachowania gdziekolwiek choćby PC przerwania trochę nie mają sensu. Jak będzie wyglądać sekwencja porzucenia głównego wątku i rozpoczęcia wykonywania ISR? Czy będą wektory w RAMie? Czy ustalone punkty wejścia? Czy wszystko wchodzi przez jeden adres? Czy będzie stos a jeśli tak to jaki? W RAMie (wskaźnik stosu?) czy sprzętowy zestaw rejestrów LIFO? A może jeden rejestr śladu? Tego też nie widać.

Jakie tryby adresowania przewidujesz? Nawet najprostsze konstrukcje ogromnie zyskują gdy mogą adresować pamięć pośrednio przez rejestr (indirect) - dopiero wtedy, gdy można wyliczać adresy daje się sensowne obsługiwać tablice itp struktury danych. Czy jakoś to przewidujesz? Moim zdaniem różnorodność metod adresowania jest nawet ważniejsza niż rozbudowane funkcje ALU. A propos: czy mógłbyś przedstawić proponowaną listę instrukcji wraz z możliwymi trybami adresowania?

Czy przewidujesz jakieś instrukcje zatrzymujące CPU choćby do przyjścia przerwania (np. IDLE) bo wiesz, obniżanie poboru mocy to dziś priorytet.

Czy mógłbyś jakoś pokrótce odpowiedzieć na powyższe zanim zrealizujesz ten projekt?

BTW: Przerwania rzeczywiście dzielimy na zewnętrzne i wewnętrzne, ale to nie jest tak jak opisałeś. Zewnętrzne to wszystkie te , które przychodzą ze źródeł niezależnych (asynchronicznych) od programu: peryferia, piny I/O itp a wewnętrzne to te, które zgłasza sobie niejako sam procesor: dzielenie przez zero, naruszenie ochrony pamięci, nieprawidłowy kod operacyjny instrukcji czy jawne wykonanie instrukcji przerwania (np. zmieniającego poziom przywilejów lub robiącego breakpoint).

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Na ten moment komunikacja z rejestrami realizowana jest właśnie za pomocą osobnych instrukcji. Myślę że w przyszłych projektach wszystko realizowane będzie właśnie na zasadzie większych bloków a komunikacja zostanie ujednolicona. 

W mojej wizji rejestr A pełni funkcję czegoś na wzór rejestru wejściowego. Dane na początek zapisywane są tam dopiero później rozsyłane są dalej. W taki sam sposób przesyłany jest rozkaz dla ALU zapisywany docelowo w rejestrze C. Jako iż wszystko działa w sposób wspomniany wyżej nie mamy możliwości zmiany zawartości rejestru poprzez jeden rozkaz. Należy wprowadzić odpowiedni rozkaz dla ALU a następnie wynik zapisać w docelowym rejestrze.

Procesor jest 8 bitowy.

Pamięć ROM posiada strukturę 2048x16 natomiast RAM 256x8.

Raczej kluczowa jest zajętość FPGA. Układ na płytce Elbert v2 nie dysponuje dużą ilością zasobów, a przy syntezie prostszego projektu tak wygląda ich zużycie. 

Przechwytywanie.thumb.PNG.18beb80c67f6b50e362b46f6dd7c59c3.PNG

Co do układu przerwań to będę eksperymentował. Na początek myślę że zaprojektuje coś prostego na zasadzie wystąpi przerwanie->skok do podprogramu->powrót. Na razie świadomie rezygnuje z zabezpieczenia się przed sytuacjami typu w trakcie obsługi przerwania wystąpi kolejne.

Adresowanie bezpośrednie, listę instrukcji umieszczę wkrótce.

Na razie funkcji zatrzymania CPU nie planuję.

BTW: Uwaga co do przerwań jak najbardziej słuszna. 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
11 minut temu, FlyingDutch napisał:

czasami same zapamiętanie wartości PC (adresu powrotu) nie wystarcza

W zasadzie wystarcza, bo z punktu widzenia sprzętu PC to jedyny zasób którego program rozpoczynający wykonywanie ISR nie jest w stanie odtworzyć. Jak rozumiem miałeś na myśli "niewystarczanie:" w sensie niszczenia ogólnego stanu procesora w trakcie wykonywania obsługi przerwania. Niestety tu mamy kolejny przykład ważnej decyzji: jeżeli sprzęt będzie zapisywał na stos dużo rzeczy, to będzie to kosztowało dużo czasu więc reakcja procesora na przerwanie będzie wolna. Pierwsza instrukcja naszego kodu ISR wykona się dopiero po dokonaniu iluś tam zapisów do pamięci, a nie zawsze jest to konieczne. Można sobie wyobrazić przerwanie, którego obsługa składa się z jednej instrukcji, np. mając do dyspozycji wiele rejestrów, jeden z nich można globalnie zarezerwować na wskaźnik w jakimś buforze. Przykładowy ADC zgłasza koniec konwersji, sprzęt zapisuje tylko PC na stos, wykonuje pierwszą instrukcję z ISR która polega na a) przeładowaniu słowa danych z ADC do pamięci pod adres umieszczony w umówionym rejestrze i b) jego automatycznej postinkrementacji, po czym następuje powrót do przerwanego wątku (instrukcja RET lub RETI odblokowujące system przerwań). To oczywiście skrajny przykład, ale pokazujący, że być może przerzucenie na sprzęt zapisywania stanu całego procesora jest przesadą. Architektura oraz lista instrukcji procesora muszą być jednak przemyślane tak, by wspierały takie rozwiązania. Co więcej, PC nie musi być zapisywany na stos.Jeśli zrobisz rejestr leżący tuż obok PC to tam możesz zapisać jego aktualną zawartość i już w następnym cyklu maszynowym zaadresować pamięć wektorem pierwszej instrukcji ISR. Wtedy to programiście (ew.kompilatorowi) zostawiasz decyzję co do zapisu kontekstu na stos. I znów: lista instrukcji musi to wspierać, bo program musi mieć dostęp do rejestru shadow-PC aby jego zawartość móc przesłać gdzieś w bezpieczne miejsce. Ten sam mechanizm, ale w drugą stronę można wykorzystać do powrotu: jakaś instrukcja zdejmuje ze stosu (lub z innego bezpiecznego miejsca) rejestr shadow-PC a właściwy RET przeładowuje to do PC. Co więcej (po raz drugi) operacje na tym drugim rejestrze nie muszą być rzeczywistą wymianą danych między PC a shadow. Wystarczy przecież zmieniać jednobitową flagę określającą który z tych rejestrów jest aktualnie prawdziwym licznikiem rozkazów. Idźmy dalej: w 8035 mamy 12-bitowy PC a 8-poziomowy stos  jest umieszczony w 8-bitowej pamięci więc podczas cyklu potwierdzania przerwania procesor zapisuje nie tylko PC ale też 4 bity (flagi) stanu ALU (tzw. PSW - Processor Status Word). Sprzęt robi zatem tylko to co niezbędne a program może zrobić resztę, np. zachować akumulator. Co ciekawe mamy tam 2 banki po 8 rejestrów przełączanych jedną instrukcją więc ISR może błyskawicznie zmienić sobie kontekst na bank alternatywny, podziałać na nim a przed powrotem (specjalna instrukcja RETN) odtworzyć flagi powracając tym samym do oryginalnego banku rejestrów. I to wszystko bez użycia stosu. Co prawda zmusza to do jednopoziomowego systemu przerwań (choć są mechanizmy by to rozszerzyć), ale w małych procesorkach to się sprawdza. Potwierdzeniem tego jest rozwiązanie zastosowane w następcy, czyli 8051. Tam, dostaliśmy już 4 banki rejestrów także bardzo szybko przełączane, no i system przerwań ma 4 poziomy..

To tylko kilka przykładów na to jak bardzo architektura procesora musi być przemyślana i jak ściśle współpracuje z listą rozkazów. Robiąc taki projekt warto narysować sobie model sprzętu z punktu widzenia programisty: bez tych wszystkich strzałek, magistral czy multiplekserów. Mamy wtedy czysty obraz dostępnych rejestrów, mapę pamięci itp i wtedy porównać to z planowaną listą instrukcji, która nie powinna być kulą u nogi a szansą na sprawną realizację algorytmów. A żeby taką się stała trzeba zwyczajnie napisać trochę nietrywialnych funkcji/programów i zmodyfikować ja tak, by w danej architekturze była optymalna. W prostszych przypadkach wystarczy mieć trochę doświadczenia 😀 bo na tym najniższym poziomie procesory robią jakieś bardzo podstawowe rzeczy: przesuwają bloki pamięci, porównują je, liczą arytmetykę wielokrotnej precyzji, odliczają pętle for z liczników czy wołają podprogramy z przekazywaniem do nich parametrów i odzyskiwaniem wyników. W każdym z takich przypadków architektura i lista instrukcji muszą ze sobą ściśle współgrać.

BTW: Osobnym problemem jest właśnie. przekazywanie argumentów do funkcji. Nie licząc jakichś egzotycznych pomysłów, robimy to przez stos albo przez rejestry. W każdym z przypadków potrzebne są instrukcje do sprawnej obsługi "stałych fragmentów gry": tworzenia, dostępu do i niszczenia ramek na stosie. W przypadku stosu przydaje się adresowanie bazowe z możliwością albo adresowania względem SP (np. coś w rodzaju MOV A, [SP+6] ) albo przeładowania jego zawartości do któregoś z rejestrów ogólnego przeznaczenia. Jest to szczególnie ważne w procesorach z małą liczbą rejestrów.

Moim zdaniem minimalna architektura 8-bitowej maszynki to coś w rodzaju Motoroli 6800 (dwa akumulatory i jeden rejestr indeksowy) albo któryś z jego "dzieci" np. MOS Technology 6502 (jeden akumulator ale za to dwa rejestry indeksowe). Nie ma co wyważać dawno otwartych drzwi i po prostu podglądać jakie sprytne rzeczy powymyślali projektanci tamtych układów. W szczególności chodzi mi o tryby adresowania: stronę zerową i dwie zupełnie różne koncepcje używania rejestrów indeksowych.

 

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites
(edytowany)
31 minut temu, marek1707 napisał:

1) W zasadzie wystarcza, bo z punktu widzenia sprzętu PC to jedyny zasób którego program rozpoczynający wykonywanie ISR nie jest w stanie odtworzyć. Jak rozumiem miałeś na myśli "niewystarczanie:" w sensie niszczenia ogólnego stanu procesora w trakcie wykonywania obsługi przerwania. Niestety tu mamy kolejny przykład ważnej decyzji: jeżeli sprzęt będzie zapisywał na stos dużo rzeczy, to będzie to kosztowało dużo czasu więc reakcja procesora na przerwanie będzie wolna.

2) Moim zdaniem minimalna architektura 8-bitowej maszynki to coś w rodzaju Motoroli 6800 (dwa akumulatory i jeden rejestr indeksowy) albo któryś z jego "dzieci" np. MOS Technology 6502 (jeden akumulator ale za to dwa rejestry indeksowe). Nie ma co wyważać dawno otwartych drzwi i po prostu podglądać jakie sprytne rzeczy powymyślali projektanci tamtych układów. W szczególności chodzi mi o tryby adresowania: stronę zerową i dwie zupełnie różne koncepcje używania rejestrów indeksowych.

 

Cześć Marek,

1) Myślałem o automatycznym zapamiętaniu na stosie jedynie wartości PC (adres powrotu). Oraz zaimplementowaniu instrukcji CPU: PUSH rejestr i POP rejestr (niech programista sam decyduje, czy poza adresem powrotu chce zapamiętać coś jeszcze.

2) Akurat Intel8080 i MOS6502 to dwa pierwsze procesory, które poznałem. Intel 8080 był w "walizkowym" komputerku z klawiaturą heksadecymalną i diodkami LED na magistralach adresowych i danych (to był pierwszy układ z CPU do jakiego miałem dostęp w drugiej klasie technikum w pracowni informatycznej). Zaczynałem naukę programowania właśnie od programowania w assemblerze Intela 8080 - rozkazy tłumaczyło się "ręcznie" korzystając z tabelki w książce (lista rozkazów intela 8080) i wprowadzało jako hex'y z klawiatury. MOS 6502 pojawił się w pracowni informatycznej rok później razem z komputerami Atari800-XL. Jego też programowałem najpierw w assemblerze, a potem w assemblerze i Basic'u (razem).

Zgadzam się z Tobą, że jego architektura (MOS6502) była dużo lepsza od Intela 8080 (mało rejestrów, dużo trybów adresowania, strona zerowa pamięci i uproszczona lista rozkazów), Trzeba jednak brać pod uwagę, że Rafał dopiero zaczyna swoją przygodę z budową własnego CPU i rozumiem, że zaczyna implementację od najprostszych rozwiązań. Nie sądzę, aby kiedykolwiek pisał kompilator np. języka C do swojego CPU 😉

Dużo można podpatrzeć (budowa soft-procesorów) na stronie Opencores.org, tam są projekty z kilkudziesięcioma soft-cpu od 4-bitowych do OpenSparc.

Pozdrawiam.

 

Edytowano przez FlyingDutch
  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

W żadnym razie nie chodziło mi o niszczenie zaangażowania Kolegi @rafal2808 a raczej o pokazanie, a) że jest mnóstwo dobrych przykładów, b) że nawet prosty procesor jest o wiele bardziej skomplikowanym projektem niż się wydaje na pierwszy rzut oka. Siadając do komputera mamy zwykle ogromną ochotę od razu zacząć pisać program albo tworzyć kod VHDL a nie tędy droga. Bez przemyślenia na pewno też coś powstanie, ale bez żartów - trudno będzie nazwać procesorem coś wykonującego kilka przypadkowo zmyślonych instrukcji składających się na mruganie LEDem. OK, to pewnie spełnia wymogi formalne definicji słowa procesor, ale spełnia je także maszynka do drylowania wiśni. I dlatego chętniej zobaczyłbym projekt architektury (ten z punktu widzenia programisty) i listę instrukcji - bo to daje już posmak tego co można będzie zrobić pisząc pierwsze programy - niż obrazek z licznikami, pamięciami i strzałkami a co gorsza od razu kod VHDL. Rozumiem, że to nie wyścig na Księżyc i jako amatorzy musimy zaczynać od prostych rzeczy, ale nawet robiąc coś 4- czy nawet 1-bitowego dobrze jest mieć jakiś plan. Nie polegający na zwykłym "zrobię sobie procesor", tylko jakiś bardziej precyzyjny w szerszym kontekście. Inaczej nauka języka i konkretnego środowiska (bo jak rozumiem głównie o to tu chodzi) nie będzie efektywna. A jedynym sprawdzianem Twoich umiejętności jest przecież porównanie. Możesz całą zimę spędzić na siłowni i basenie, od marca przesiąść się na rower i z kopyta ostro jeździć ale cały ten trening będzie nic nie wart dopóki nie porównasz swoich wyników z osiągnięciami innych ludzi. Dlatego dopiero wyścig weryfikuje co umiesz. Tutaj tak samo: pomysł typu "zrobię 4004 wykonujący jego natywny kod binarny" jest dobrym wyzwaniem, ale dużo lepszym jest "ale w mniejszej liczbie taktów zegara" (co akurat w przypadku tego 16-pinowego CPU z 4-bitowym kontaktem ze światem jest bardzo proste). A w przypadku architektur własnych - jak ta tutaj - świetną oceną mogą być benchmarki starych procesorów. Co więcej, były one wręcz stworzone do pisania w językach asemblera więc nauczenie się kilku z nich choćby dla potrzeb stworzenia własnych testów prędkości byłoby kolejnym plusem tej zabawy. Jeżeli Twoje 8-bitowe cudo zrobione na nowoczesnym FPGA i używające w zasadzie nieograniczonej liczby bramek będzie wolniej (w sensie liczby taktów zegara) sortowało blok pamięci albo wolniej liczyło iloraz dwóch liczb 32-bitowych lub zmiennoprzecinkowych niż 8080 to wiesz, nie ma się czym chwalić ani z czego cieszyć. Gdzieś popełniłeś poważne błędy w projekcie: albo w architekturze, albo w liście instrukcji a goście sprzed kilkudziesięciu lat zrobili to zwyczajnie lepiej. No chyba, że założenie było inne, np. "moje 8-bitowe CPU będzie miało wyłącznie 1-bitowe magistrale szeregowe" - bo takie procesory też były, albo może "mój procesor będzie natywnie wykonywał kod Brainfuck'a" - to też już było, albo "skoro każdy układ logiczny można zsyntetyzować przy użyciu bramek NAND to moje CPU będzie miało1-bitowe ALU z jedną instrukcją - zgadnijcie jaką". Cóż, zmyślam na poczekaniu...

Podsumowując: nie widzę sensu w implementacji czegokolwiek (niechby i procesora) bez postawienia sobie jasnych i weryfikowalnych założeń. Nie muszą to być wyzwania z kosmosu, ale na pewno nic trywialnego lub mglistego. I chętnie o takich założeniach mogę dyskutować.

A tej uwagi o kompilatorze C nie bardzo rozumiem. Tworzenie kompilatorów jest dzisiaj zautomatyzowane, ale projektant sprzętu nie musi umieć ich pisać. Od tego są inni ludzie. Ma zrobić procesor, który nie ma wąskich gardeł i spełnia założenia (np. jeśli ma być programowany głównie w C to ma się niego dobrze pisać kompilatory - to też widać w architekturze) natomiast może i powinien przetestować swój procesor na wielu typowych programach/testach a do tego wystarczy asembler. Zaznaczam, że wciąż mówimy o małych, jednowątkowych maszynkach do nauko-zabawy w domu.

BTW: Chyba pamiętam ten walizkowy system na 8080.

EDIT: --------------------------------------

"Zgadzam się z Tobą, że jego architektura (MOS6502) była dużo lepsza od Intela 8080.."

Hej, nigdzie tego nie napisałem. Wspomniałem o minimalnej, czyli prostej do implementacji nawet dla początkującego a dającej już frajdę w pisaniu programów.

Edytowano przez marek1707
  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...