Symulacje czasowe i ograniczenia czasowe

 

W ramach ćwiczenia należy obowiązkowo użyć gotowego bloku kontrolera testującego pamięć statyczną RAM umieszczoną na płytce Spartan-3 Starter Kit Board (opis na str. 11 instrukcji). Użyta pamięć to układ IS61LV25616AL. Na płytce dostępne są dwa układy scalone SRAM połączone równolegle i tworzące w ten sposób pamięć o organizacji 262144 słów 32 bitowych. Kontroler pamięci, który dostępny jest pod tym linkiem,  działa następująco:

-         zapis do każdej komórki tego samego słowa 32 bitowego „01_01_01_..._01” ,

-         odczyt każdej komórki pamięci i sprawdzenie czy odczytana wartość jest równa uprzednio wgranej,

-         zapis do każdej komórki tego samego słowa 32 bitowego „10_10_10_..._10” ,

-         odczyt każdej komórki pamięci i sprawdzenie czy odczytana wartość jest równa uprzednio wgranej,

-         wykonywanie powyższych czynności odbywa się cyklicznie w nieskończonej pętli,

-         sygnały wyjściowe kontrolera: good_count oraz bad_count zawierają liczbę cykli testowania wykonanych odpowiednio bez błędu i z błędem, są to wektory zawierające liczby w kodzie dwójkowym,

-         stała test_end deklaruje ostatni testowany adres pamięci, po jego osiągnięciu test rozpoczynany jest od adresu zerowego, dla symulacji należy testować 8 pierwszych adresów, dla implementacji całą przestrzeń adresową.

 

Blok testera pamięci należy osadzić w taki sposób aby:

-         na wyświetlaczu LED płytki Spartan-3 były umieszczone: na 3 lewych pozycjach liczba prawidłowo wykonanych, na prawej pozycji liczba nieprawidłowych cykli, dla uproszczenia projektu należy wyświetlać cyfry w formacie szesnastkowym,

-         dodatkowo przełącznik SW7 (port sw7_i) powinien przełączać taktowanie bloku testera pamięci z częstotliwością równą 50MHz lub 120MHz, zegar 120MHz należy uzyskać poprzez wstawienie bloku DCM i wykorzystanie wyjścia CLKFX, jest to wyjście z syntezera częstotliwości zawartego w bloku DCM.

 

Po wstępnym wykonaniu opisu układu należy wykonać symulację funkcjonalną. Następnie należy wykonać implementację i kolejną symulację po implementacji obserwując  sygnały dochodzące do pamięci RAM. Należy wgrać układ na płytkę FPGA i zaobserwować czy występują błędy pamięci RAM dla obu położeń przełącznika SW7.

 

Kolejnym krokiem jest powtórzenie poszczególnych symulacji oraz testowania płytki po dodaniu do pliku UCF ograniczeń czasowych.

           

            W czasie obu implementacji należy zapisać wartość otrzymanego najgorszego opóźnienia sieci (Raport Asynchronus Delay Raport).

 

Dodatek - opis jak wykonać poszczególne kroki:

 

A) Dodanie bloku DCM. Pełna dokumentacja dotycząca bloku DCM znajduje się w dokumencie katalogowym układu SPARTAN3 (str. 65). Aby nie zagłębiać się w szczegóły i szybko dodać taki blok należy wykonać kolejne kroki:

-         dodać deklarację bibliotek nad blokiem ENTITY w którym chcemy osadzić DCM:

library UNISIM;

use UNISIM.VComponents.all;

-         osadzić komponent poprzez PORT MAP () podobnie jak w załączonym pliku, wartości parametrów generic definiują właściwości wstawianego bloku,

plik z przykładowym osadzeniem

-         aby blok DCM w symulacji prawidłowo generował sygnały wyjściowe, dołączony do niego reset asynchroniczny powinien być wystarczająco długi (przynajmniej 3 okresy zegara wejściowego).

 

B) Model pamięci. Jako model pamięci do symulacji projektu należy pobrać kod z tego linku i osadzić odpowiednio w pliku testbench. Jest to prosty model funkcjonalny zawierający tylko jedno opóźnienie – czas dostępu w czasie odczytu. W celu uniknięcia długiego czasu symulacji zaleca się zmodyfikować stałą test_end (tylko do symulacji). Liczbę słów dostępnych w modelu pamięci deklaruje się poprzez generic words i domyślnie jest on ustawiony na 8.

 

C) Symulacja czasowa po implementacji. Symulację czasową po implementacji wykonuje się podobnie jak symulację funkcjonalną. Różnicą jest to że:

-         przed symulacją w oknie Sources należy wybrać Post-Route Simulation

     

-         następnie w oknie Processes należy kliknąć w przycisk Simulate Post Place & Route Model

-         po chwili uruchomi się symulator i dalej należy postępować jak dla symulacji funkcjonalnej, w stosunku do symulacji funkcjonalnej na wykresie czasowym pojawią się opóźnienia sygnałowe, również nazwy sygnałów wewnętrznych tracą hierarchię i są nieco trudniejsze do odnalezienia.

 

D) Ograniczenia (wymagania) czasowe. Jest kilka metod zadawania w środowisku ISE wymagań czasowych jakie projektant układu chciałby uzyskać po implementacji. Jedną z nich jest umieszczenie ograniczeń w pliku UCF. Szczegóły można znaleźć w dokumencie Constraints Guide lub Timing Constraints Guide. W ramach niniejszego ćwiczenia należy umieścić ograniczenia czasowe przygotowane wcześniej i obejmujące sygnały łączące układ FPGA z pamięcią RAM. Plik UCF ograniczeń czasowych zawarty jest na końcu niniejszego opisu. Po implementacji projektu można przeglądać raporty związane z opóźnieniami układu.

 

W przypadku gdy projekt zawiera ograniczenia czasowe, w czasie implementacji w konsoli ISE wyświetlane są komunikaty ogólne dotyczące tych ograniczeń. W przypadku braku spełnienia wymagań czasowych można prześledzić ścieżki krytyczne z niespełnionymi czasami. Wykonuje się to poprzez wywołanie programu Timing Analyzer klikając jak na załączonym rysunku (program też można uruchomić poza środowiskiem ISE):

 

Następnie w zakładce Sources/Timing na czerwono wyróżniane są niespełnione wymagania czasowe. Kliknięcie na ścieżce podświetlonej na czerwono wyświetla w oknie głównym podsumowanie założonych wymagań oraz szczegóły przejść sygnałów. Można następnie kliknąć w niebieskie łącza w celu uzyskania szczegółów dotyczących poszczególnych opóźnień.

 

Plik UCF:

# Clock:

NET "clk_i" LOC = "T9" ; # 50 MHz clock

# Push-buttons:

NET "rst_i" LOC = "L14" ; # pressed high BTN3

# SW7:

NET "sw7_i" LOC = "K13" ; # slider sw7

# 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

# SRAM pinout

# Address bits common for both chips

NET "addr_o<17>" LOC = "L3" ;

NET "addr_o<16>" LOC = "K5" ;

NET "addr_o<15>" LOC = "K3" ;

NET "addr_o<14>" LOC = "J3" ;

NET "addr_o<13>" LOC = "J4" ;

NET "addr_o<12>" LOC = "H4" ;

NET "addr_o<11>" LOC = "H3" ;

NET "addr_o<10>" LOC = "G5" ;

NET "addr_o<9>" LOC = "E4" ;

NET "addr_o<8>" LOC = "E3" ;

NET "addr_o<7>" LOC = "F4" ;

NET "addr_o<6>" LOC = "F3" ;

NET "addr_o<5>" LOC = "G4" ;

NET "addr_o<4>" LOC = "L4" ;

NET "addr_o<3>" LOC = "M3" ;

NET "addr_o<2>" LOC = "M4" ;

NET "addr_o<1>" LOC = "N3" ;

NET "addr_o<0>" LOC = "L5" ;

# Data and control bits

# IC10

NET "data_io<31>" LOC = "R1" ; # IC 10 IO15

NET "data_io<30>" LOC = "P1" ; # IC 10 IO14

NET "data_io<29>" LOC = "L2" ; # IC 10 IO13

NET "data_io<28>" LOC = "J2" ; # IC 10 IO12

NET "data_io<27>" LOC = "H1" ; # IC 10 IO11

NET "data_io<26>" LOC = "F2" ; # IC 10 IO10

NET "data_io<25>" LOC = "P8" ; # IC 10 IO9

NET "data_io<24>" LOC = "D3" ; # IC 10 IO8

NET "data_io<23>" LOC = "B1" ; # IC 10 IO7

NET "data_io<22>" LOC = "C1" ; # IC 10 IO6

NET "data_io<21>" LOC = "C2" ; # IC 10 IO5

NET "data_io<20>" LOC = "R5" ; # IC 10 IO4

NET "data_io<19>" LOC = "T5" ; # IC 10 IO3

NET "data_io<18>" LOC = "R6" ; # IC 10 IO2

NET "data_io<17>" LOC = "T8" ; # IC 10 IO1

NET "data_io<16>" LOC = "N7" ; # IC 10 IO0

NET "ce10_o" LOC = "P7" ; # chip enable of IC10

NET "ub10_o" LOC = "T4" ; # upper byte enable of IC10

NET "lb10_o" LOC = "P6" ; # lower byte enable of IC10

# IC11

NET "data_io<15>" LOC = "N1" ; # IC 11 IO15

NET "data_io<14>" LOC = "M1" ; # IC 11 IO14

NET "data_io<13>" LOC = "K2" ; # IC 11 IO13

NET "data_io<12>" LOC = "C3" ; # IC 11 IO12

NET "data_io<11>" LOC = "F5" ; # IC 11 IO11

NET "data_io<10>" LOC = "G1" ; # IC 11 IO10

NET "data_io<9>"  LOC = "E2" ; # IC 11 IO9

NET "data_io<8>"  LOC = "D2" ; # IC 11 IO8

NET "data_io<7>"  LOC = "D1" ; # IC 11 IO7

NET "data_io<6>"  LOC = "E1" ; # IC 11 IO6

NET "data_io<5>"  LOC = "G2" ; # IC 11 IO5

NET "data_io<4>"  LOC = "J1" ; # IC 11 IO4

NET "data_io<3>"  LOC = "K1" ; # IC 11 IO3

NET "data_io<2>"  LOC = "M2" ; # IC 11 IO2

NET "data_io<1>"  LOC = "N2" ; # IC 11 IO1

NET "data_io<0>"  LOC = "P2" ; # IC 11 IO0

NET "ce11_o" LOC = "N5" ; # chip enable of IC11

NET "ub11_o" LOC = "R4" ; # upper byte enable of IC11

NET "lb11_o" LOC = "P5" ; # lower byte enable of IC11

# common control bits

NET "oe_o" LOC = "K4" ; # output enable

NET "we_o" LOC = "G3" ; # write enable

#

 

Uzupełnienie pliku UCF o ograniczenia czasowe:

# deklaracja nazwy specyfikacji jako TNM_NET = clk_in

NET "clk_i" TNM_NET = "clk_in" ;

# deklaracja zegara jako 20ns o wypełnieniu 50%

# oznacza to, że układ będzie tak implementowany, że zachowane zostaną ograniczenia

# czasowe wewnątrz rdzenia tak aby przerzutniki dołączone do clk_i

# miały spełnione czasy setup i hold

# dla 20ns zegara

TIMESPEC "TS_clk_i" = PERIOD "clk_in" 20ns HIGH 50%;

# ograniczenie na zegar CLKFX po wstawieniu DCM jest automatycznie dodawane przez implementator

# opóźnienie globalne - ustalenie, że wszystkie sygnały generowane na zewnątrz mają

# być opóźnione o 30ns w stosunku do zegara clk_i

OFFSET = OUT 30ns AFTER "clk_i";

# opóźnienie globalne - ustalenie, że wszystkie sygnały pobierane z zewnątrz

# są o 30ns szybciej niż zegar clk_i

OFFSET = IN 30ns BEFORE "clk_i";

# sprecyzowanie opóźnień dla wyprowadzeń asynchronicznych

# najpierw sygnały zostały zgrupowane w grupy o nazwach SIG_OUT SIG_IN a następnie utworzono

# ograniczenie czasowe dla tej grupy

TIMEGRP "SIG_OUT" = PADs ("addr_*" "data_*" "we_o" "oe_o");

TIMESPEC TS01 = from FFS to SIG_OUT 8ns;

TIMEGRP "SIG_IN" = PADs ("data_*");

TIMESPEC TS02 = from SIG_IN to FFS 3ns;