Zadanie: Należy rozbudować poprzedni projekt w następujący
sposób:
System powinien buforować
dane odbierane z portu RS232, a następnie po zebraniu określonej liczby
(row_max) znaków wydrukować je z powrotem do portu RS232 w formie
„pseudograficznej” (używając jako „pixela” znaku o takim samym kodzie jak
drukowany). Oprócz tego projekt powinien buforować znaki przychodzące z wejścia
RS232 w czasie kiedy drukowana jest linia (i nie można ich na bieżąco
drukować). Długość bufora: 64 znaki. Przekroczenie bufora powinno być
sygnalizowane zapaleniem się LEDy LD0. Parametry portu RS232 oraz funkcja
monitorowania odebranych znaków (przed buforowaniem) powinny pozostać tak jak w
poprzednim ćwiczeniu. Parametr row_max powinien być łatwy do modyfikacji (np.
ustalany jako stała). Akceptowane wartości: 2 do 15.
Poniżej podano przykład
wydruku kilku linii (row_max=8):
bbb ddd fff hhh
bb dd ff ff hh
bb dd
ff f hh
aaaa bbbbb
cccc dddd eeee
ff ggg gg hh
aa bb
bb cc cc dd dd ee
ee fffff gg
gg hhhhh
aaaaa bb
bb cc dd dd eeeeee
ff gg gg
hh hh
aa aa bb
bb cc dd dd ee
ff gg gg
hh hh
aa aa bbb bb
cc cc dd dd ee
ee ff ggggg hh hh
aaa aa bbb
bb cccc ddd dd eeee ffff
gg hhh hh
gg gg
gggg
AA BBBBBB
CCC DDDDD EEEEEE
FFFFFF GGGG HH
HH
AAAA BB
BB CC CC DD DD
EE E FF F GGG GG
HH HH
AA AA
BB BB CC C DD
DD EE FF GG HH
HH
AA AA
BB BB CC DD DD
EE E FF F GG
HH HH
AA AA
BBBBB CC DD
DD EEEE FFFF
GG HHHHHH
AAAAAA BB
BB CC DD DD EE E
FF F GG GGG HH
HH
AA AA
BB BB CC C DD
DD EE FF GG GG
HH HH
AA AA
BB BB CC CC DD DD EE
E FF GGG GG HH HH
AA AA BBBBBB CCC DDDDD EEEEEE
FFFF GGGG HH
HH
11 2222
3333 44 555555
666 777777 8888
111 22
22 33 33 444 55
66 77 77
88 88
1111 22 33 4444
55 66 77
88 88
11 22 33 44 44 55
66 77 88
88
11 22
3333 44 44
55555 66666 77
8888
11 22 33 4444444
55 66 66 77 88
88
11 22 33 44
55 66 66 77 88
88
11 22
22 33 33 44 55
55 66 66 77 88
88
1111 222222
3333 4444 5555
6666 77 8888
Znaki o kodach poniżej 32
powinny być drukowane przy użyciu gwiazdek jako „pixeli”. Nie należy wprowadzać
kontrolnej funkcji tych znaków (np. końca linii itp.). Przykład linii
zawierającej znaki o kodach 0-31:
**** ****
***** ***** ***** ***
* *
** ** *** * *
******* ** ** * *****
**** **
** ** * * * * * ** * **
******* *** *****
* **
** ** * *
* ******* ******* ***** ***
* ****
**** * * ******* ******* ******* *******
* **
** ** * *** * ** **
******* ***** *******
** ******
** ** * * * *** ***
***** *** *****
***
** ** **
* * ******* ***
* *
**
** **** *****
***** * *****
Wejścia i wyjścia układu:
clk_i - zegar 50MHz,
rst_i - reset asynchroniczny,
RXD_i - wejście danych RS232,
TXD_o - wyjście danych RS232,
ld0 – wyjście sygnalizacji przepełnienia bufora FIFO
(LED LD0),
led7_an_o – wyjście sterujące anodami wyświetlaczy LED,
led7_seg_o – wyjście sterujące segmentami wyświetlaczy LED.
Do realizacji projektu
potrzebne będą dwa moduły funkcjonalne wygenerowane przez Core Generator.
Pamięć ROM posłuży do przechowywania kształtów znaków zaś pamięć FIFO posłuży
do implementacji 64-znakowego bufora FIFO buforującego port wejściowy RS-232.
Definicje znaków 8x16 pixeli są dostępne w pliku: chargen.coe
Generacja modułu pamięci ROM:
Wybieramy: Project -> New Source
Następnie otwiera się okienko Select
Source Type w którym wybieramy:
IP (CORE Generator & Architecture Wizard).
Podajemy nazwę pliku dla nowego komponentu (generowanego przez Core Generator),
np. char_mem i klikamy Next:
Następnie otwiera się okienko wyboru
nowego komponentu Select IP:
W okienku tym wybieramy Block Memory
Generator v2.8 oraz klikamy Next. Następnie wyświetla się okienko
podsumowania: New Source Wizard – Summary. Należy potwierdzić naciskając
Finish.
Następnie uruchamia się Core Generator i
otwiera się okienko konfiguracji nowego komponentu:
W tym okienku (1/4) zmieniamy Memory
Type na Single Port ROM.
Znajdujący się w lewym dolnym roku przycisk View Data Sheet otworzy
szczegółową dokumentację projektowanego komponentu.
Następnie klikamy Next.
W tym okienku (2/4) zmieniamy Read
Width na 8 (definicja znaku ma w poziomie 8 pixeli) oraz Read
Depth na 4096 (jest 256 znaków po 16 bajtów każdy).
Następnie klikamy Next.
W tym okienku (3/4) zaznaczamy Load
Init File oraz wprowadzamy ścieżkę do pliku chargen.coe
z definicjami znaków (można ułatwić to sobie za pomocą przycisku Browse).
Następnie klikamy Next.
W tym okienku (4/4) nic nie zmieniamy.
Warto zapamiętać pomocnicze informacje zawierające podsumowanie właściwości
projektowanego komponentu (pole Information) oraz blokowy schemat
połączeń. Szczególnie istotna jest informacja: Total Port A Read Latency
(From Rising Edge of Read Clock): 1 Clock Cycle(s)
Należy potwierdzić naciskając Finish.
Spowoduje to rozpoczęcie generacji
komponentu – należy trochę poczekać.
Po zakończeniu generacji komponent
zostanie dołączony do listy plików projektu.
Poniżej podano sposób przechowywania
kształtów znaków w pamięci ROM (na przykładzie literki R):
Adres |
b7 |
b6 |
b5 |
b4 |
b3 |
b2 |
b1 |
b0 |
kod_ascii*16+0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
kod_ascii*16+1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
kod_ascii*16+2 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
kod_ascii*16+3 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
kod_ascii*16+4 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
kod_ascii*16+5 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
kod_ascii*16+6 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
kod_ascii*16+7 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
kod_ascii*16+8 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
kod_ascii*16+9 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
0 |
kod_ascii*16+10 |
0 |
1 |
0 |
0 |
1 |
0 |
0 |
0 |
kod_ascii*16+11 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
kod_ascii*16+12 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
kod_ascii*16+13 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
kod_ascii*16+14 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
kod_ascii*16+15 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Adres – adres w pamięci ROM.
kod_ascii – kod ASCII wyświetlanego znaku.
Adres dla pamięci ROM najłatwiej jest
wygenerować łącząc 8-bitowy wektor kodu znaku ASCII z 4-bitowym wektorem numeru
linii w znaku (0-15) za pomocą operatora &. Należy pamiętać o tym, że po
wydrukowaniu każdej linii ze znakami należy wysłać dwa znaki: CR (kod: 13) oraz
LF (kod: 10) w celu przejścia do nowej linii.
W celu ułatwienia dołączenia komponentu do
hierarchii aktualnego projektu w katalogu projektu powstają pliki *.vho
zawierające szkielet deklaracji komponentu oraz przykład jego użycia (tworzenia
instancji). Możemy te elementy łatwo przenieść do naszego projektu – wtedy
zaprojektowany komponent zajmie właściwe miejsce w hierarchii.
Przykładowy plik VHO:
--
The following code must appear in the VHDL architecture header:
------------- Begin Cut here for COMPONENT Declaration ------ COMP_TAG
component char_mem
port (
clka: IN std_logic;
addra: IN std_logic_VECTOR(11
downto 0);
douta: OUT std_logic_VECTOR(7
downto 0));
end component;
-- Synplicity black box declaration
attribute syn_black_box : boolean;
attribute syn_black_box of char_mem: component is true;
-- COMP_TAG_END ------ End COMPONENT Declaration ------------
-- The following code must appear in the VHDL architecture
-- body. Substitute your own instance name and net names.
------------- Begin Cut here for INSTANTIATION Template ----- INST_TAG
your_instance_name : char_mem
port map (
clka => clka,
addra => addra,
douta => douta);
-- INST_TAG_END ------ End INSTANTIATION Template ------------
-- You must compile the wrapper file char_mem.vhd when simulating
-- the core, char_mem. When compiling the wrapper file, be sure to
-- reference the XilinxCoreLib VHDL simulation library. For detailed
-- instructions, please refer to the "CORE Generator Help".
Tekst zaznaczony na czerwono należy skopiować do obszaru definicji
sygnałów lokalnych. Tekst zaznaczony na niebiesko
(po ustaleniu nazwy instancji komponentu oraz sygnałów podłączonych) należy
skopiować do obszaru opisu systemu. Po dokonaniu powyższych modyfikacji ikonka
komponentu powinna zająć właściwe miejsce w hierarchii plików projektu.
Modyfikacja parametrów komponentu i jego
ponowna generacja są możliwe za pomocą ikonek Manage Cores i Regenerate
Core zaznaczonych na poniższym rysunku.
Generacja modułu pamięci FIFO
W celu wygenerowania modułu bufora FIFO
postępujemy podobnie jak poprzednio.
Wybieramy: Project -> New Source
Następnie otwiera się okienko Select
Source Type w którym wybieramy:
IP (CORE Generator & Architecture Wizard).
Podajemy nazwę pliku dla nowego komponentu (generowanego przez Core Generator),
np. fifo_mem i klikamy Next:
Następnie otwiera się okienko wyboru
nowego komponentu Select IP:
W okienku tym wybieramy Fifo Generator
v4.4 oraz klikamy Next. Następnie wyświetla się okienko
podsumowania: New Source Wizard – Summary. Należy potwierdzić naciskając
Finish.
Następnie uruchamia się Core Generator i
otwiera się okienko konfiguracji nowego komponentu:
W tym okienku (1/6) wybieramy opcję: Common
Clock (CLK) – Block RAM.
Znajdujący się w lewym dolnym roku przycisk View Data Sheet otworzy
szczegółową dokumentację projektowanego komponentu.
Następnie klikamy Next.
W tym okienku (2/6) zmieniamy Write
Width na 8, Write Depth na 64 (zakładana pojemność
FIFO) oraz Read Width na 8.
Proszę zwrócić uwagę na informację na dole (zależy ona od poprzednio wybranych
opcji i oznacza liczbę cykli zegara od wydania komendy odczytu do wystawienia
danych na wyjście FIFO):
Read Latency (From Rising Edge of Read Clock): 1
Następnie klikamy Next cztery razy
(przechodzimy przez kolejne ekrany nic nie zmieniając). Ostatni ekran (6/6)
powinien wyglądać następująco:
Warto zapamiętać pomocnicze informacje
zawierające posumowanie właściwości projektowanego komponentu (pole Information)
oraz blokowy schemat połączeń.
Należy potwierdzić naciskając Finish.
Spowoduje to rozpoczęcie generacji
komponentu – należy trochę poczekać.
Podobnie jak w poprzednim przypadku należy
teraz podłączyć wygenerowany komponent do projektu (można wykorzystać
wygenerowany plik *.vho).
Ważne informacje dotyczące komponentu FIFO
można znaleźć także w następującym dokumencie: UG175
Weryfikacja układu
Należy zweryfikować układ praktycznie
poprzez zaprogramowanie płytki testowej. W razie potrzeby należy wykonać
symulację funkcjonalną. Weryfikacja praktyczna polega na sprawdzeniu działania
systemu poprzez wyświetlenie kilku linii w trybie „pseudograficznym” oraz sprawdzeniu
mechanizmu wykrywania przepełnienia bufora (np. przytrzymanie klawisza przez
dłuższy czas powinno spowodować przepełnienie bufora). Układ testować za pomocą
programu MINICOM (program jest domyślnie skonfigurowany na 9600bps, 8 bitów
danych, 1 bit stopu, bez parzystości).
Nadawanie i odbiór sygnałów w standardzie RS232
Nadawanie i odbiór sygnałów w standardzie
RS232 odbywa się w sposób szeregowy, oddzielnie na dwóch liniach w kierunkach
do i od urządzenia. W czasie braku transmisji sygnał na danej linii jest w
stanie wysokim. Rozpoczęcie transmisji inicjowane jest przez tzw. bit startu
będący "0" logicznym, który powinien trwać przez okres równy
odwrotności prędkości transmisji, w naszym przypadku wynoszący 1/9600 [sekund].
Wszystkie następne informacje są przesyłane z identycznym okresem trwania.
Następnie nadawane są szeregowo dane począwszy od najmniej znaczącego bitu aż
do bitu najbardziej znaczącego (D0-D7). Później występuje bit
parzystości, będący operacją logiczną XOR na danych D0-D7.
Bit parzystości jest opcjonalny i w przypadku niniejszego zadania nie
występuje. Zakończenie transmisji sygnalizowane jest bitami stopu, w ilości
zazwyczaj od 1 do 2. Przykład sygnału, zgodnego z wymaganiami zadania,
przenoszącego kod 01010011 przedstawiony jest na poniższym rysunku.
Rys.1 Przykładowa
transmisja kodu 01010011 przez RS-232
Informacje dodatkowe o standardzie RS232:
http://www.fizyka.umk.pl/~ptarg/labview/folie/RS232.pdf
http://pl.wikipedia.org/wiki/RS-232
Plik ucf do zadania, płytka Digilent Spartan-3, układ Spartan-3 3S200
FT256-4:
# Clock:
NET "clk_i" LOC = "T9" ; # 50 MHz clock
# Push-buttons:
NET "rst_i" LOC = "L14" ; # pressed high BTN3
# RS232:
NET "TXD_o" LOC = "R13" ; # RS 232 TXD
NET "RXD_i" LOC = "T13" ; # RS 232 RXD
# Seven-segment LED display:
NET "led7_an_o<3>" LOC = "E13" ; # leftmost digit,
active low
NET "led7_an_o<2>" LOC = "F14" ; # active low
NET "led7_an_o<1>" LOC = "G14" ; # active low
NET "led7_an_o<0>" LOC = "D14" ; # rightmost digit,
active low
NET "led7_seg_o<7>" LOC = "E14" ; # segment 'a', active
low
NET "led7_seg_o<6>" LOC = "G13" ; # segment 'b',
active low
NET "led7_seg_o<5>" LOC = "N15" ; # segment 'c',
active low
NET "led7_seg_o<4>" LOC = "P15" ; # segment 'd',
active low
NET "led7_seg_o<3>" LOC = "R16" ; # segment 'e',
active low
NET "led7_seg_o<2>" LOC = "F13" ; # segment 'f',
active low
NET "led7_seg_o<1>" LOC = "N16" ; # segment 'g',
active low
NET "led7_seg_o<0>" LOC = "P16" ; # segment 'dp',
active low
# LD0 LED:
NET "ld0" LOC = "K12" ; # high on
#