Projektowanie systemów cyfrowych w językach opisu sprzętu Studium Zaoczne IV rok kierunek Elektronika Wykład 4
Program wykładu Predefiniowane typy danych Typy rozszerzone (Extended Types) Typy wyliczeniowe (Enumerated Types) Podtypy (Subtypes) Typy złożone (Composite Types) Tablice (Arrays) Rekordy (Records) Inne typy predefiniowane Pliki (Files) Linie (Lines) 2
Predefiniowane typy danych package standard is type boolean is (false, true); type bit is ('0', '1'); type character is ( nul, soh, stx, etx, eot, enq, ack, bel,...... o', p', r', s', t', u', w',... ); type severity_level is (note, warning, error, failure); type integer is range -2147483647 to 2147483647; type real is range -1.0E308 to 1.0E308; type time is range -2147483647 to 2147483647 units fs; ps = 1000 fs; ns = 1000 ps; us = 1000 ns; ms = 1000 us; sec = 1000 ms; min = 60 sec; hr = 60 min; end units; 3
Predefiniowane typy danych subtype delay_length is time range 0 fs to time'high impure function now return delay_length; subtype natural is integer range 0 to integer'high; subtype positive is integer range 1 to integer'high; type string is array (positive range <>) of character; type bit_vector is array (natural range <>) of bit; type file_open_kind is ( read_mode, write_mode, append_mode); type file_open_status is ( open_ok, status_error, name_error, mode_error); attribute foreign : string; end standard; 4
Typy wyliczeniowe W deklaracji występuje lista nazw lub wartości definiujących nowy typ. Jest dogodnym środkiem np. dla symbolicznej reprezentacji kodów. Składnia: type identifier is (item {, item}); item: {character_literal identifier} Przykłady: literals: identifiers: type fiveval is (?, 0, 1, Z, X ); type light is (red, yellow, green); type instr is (load, store, add, sub); 5
Typy wyliczeniowe architecture behave of cpu is type instr is (add, lda, sta); --inne obiekty i typy (signal, itp) begin... process zzz is (...) variable a, b, data: integer; variable opcode: instr; begin case opcode is when lda => a := data; when sta => data := a; when add => a := a + data; end case; wait on data; end process zzz; end architecture behave; Kodowanie elementów listy dla syntezy: 00 add 01 lda 10 sta 11 --- type instr is (add,lda,sta,invalid); 6
Typy wyliczeniowe automaty stanów 7
Typy wyliczeniowe automaty stanów architecture transmit of transmit is -- diagram signals declarations signal DATA_REG: STD_LOGIC_VECTOR (9 downto 0); signal T_DATA_CNT: INTEGER range 0 to 11; signal T_TIME_CNT: INTEGER range 0 to 200; -- ONE HOT ENCODED state machine: TX_RS232 type TX_RS232_type is (NEW_BYTE, SEND_BIT, START_TX, WAIT_T_BIT); attribute enum_encoding : string; attribute enum_encoding of TX_RS232_type: type is "0001" & -- NEW_BYTE "0010" & -- SEND_BIT "0100" & -- START_TX "1000" ; -- WAIT_T_BIT signal TX_RS232: TX_RS232_type; W tutorialu kodowanie binarne, tutaj one-hot. begin -- concurrent signals assignments -- diagram ACTIONS ---------------------------------------------------------------------- -- Machine: TX_RS232 ---------------------------------------------------------------------- TX_RS232_machine: process (CLK, reset) begin if RESET='0' then TX_RS232 <= START_TX; -- Set default values for registered outputs/signals and for variables --... T_TIME_CNT <= 0; T_DATA_CNT <= 0; DATA_REG <= "0000000000"; TxD <= '1'; elsif CLK'event and CLK = '1' then -- Set default values for registered outputs/signals and for variables 8
Typy wyliczeniowe automaty stanów TX_RS232_machine: process (CLK, reset) begin if RESET='0' then TX_RS232 <= START_TX; -- Set default values for registered outputs/signals and for variables --... T_TIME_CNT <= 0; T_DATA_CNT <= 0; DATA_REG <= "0000000000"; TxD <= '1'; elsif CLK'event and CLK = '1' then -- Set default values for registered outputs/signals and for variables --... case TX_RS232 is when NEW_BYTE => if READY='1' then TX_RS232 <= SEND_BIT; T_TIME_CNT <= 0; DATA_REG <= '1'&DATA&'0'; T_DATA_CNT <= 0; elsif READY='0' then TX_RS232 <= NEW_BYTE; end if; when SEND_BIT => TxD <= DATA_REG(T_DATA_CNT); T_DATA_CNT <= T_DATA_CNT+1; TX_RS232 <= WAIT_T_BIT; T_TIME_CNT <= 0; when START_TX => T_TIME_CNT <= 0; T_DATA_CNT <= 0; DATA_REG <= "0000000000"; TxD <= '1'; if RESET='1' then TX_RS232 <= NEW_BYTE; end if;... 9
Wyrażenia kwalifikujące W niektórych przypadkach nie można rozróżnić typu literałów n.p.: char 1, bit 1 czy std_logic 1. Są one niejednoznaczne. Stosuje się wówczas tzw. narzucanie typów (type casting). Składnia: type (literal expression); Przykłady: bit ( 1 ) function ToInt (d: bit_vector) return (integer); function ToInt (d: std_logic_vector) return (integer); ToInt( 1010 ); --??? ToInt(bit_vector ( 1010 )); -- OK! 10
Konwersje typów VHDL jest rygorystyczny pod względem typów, tzn. nie pozwala na przypisywanie danemu obiektowi wartości właściwych innym typom, niż zadeklarowany dla obiektu. Funkcje konwersji typów mogą być przydatne przy dopasowywaniu dwóch projektów, które używają różnych typów. Przykład: process...; variable abc: fourval; variable xyz: value4; begin xyz := convert4val (abc); -- wywołanie funkcji end process; 11
Konwersje typów type fourval is ( X, L, H, Z ); type value4 is ( X, 0, 1, Z ); function convert4val (s: fourval) return value4 is begin case s is when X => return X ; when L => return 0 ; when H => return 1 ; when Z => return Z ; end case; end convert4val; 12
Konwersje typów Często zdarza się potrzeba konwersji pomiędzy typem integer a typem std_logic_vector. Pakiety: IEEE.std_logic_unsigned i IEEE.std_logic_arith zawierają odpowiednie funkcje konwersji: function conv_integer (arg: std_logic_vector) return integer; function conv_std_logic_vector (arg: integer; size: integer) return std_logic_vector; Przykład: entity sel is port (a,b,s: in integer range 0 to 15; q: out std_logic_vector (3 downto 0)); end; dummy function architecture good of sel is begin q <= conv_std_logic_vector(a,4) when conv_integer(s) = 8 else conv_std_logic_vector(b,4); end; 13
Konwersje typów Gdyby wszystkie sygnały były typu std_logic_vector, kod byłby bardziej zwięzły: architecture better of sel is begin q <= a when conv_integer(s) = 8 else b; end; Jeżeli narzędzie do syntezy pozwala na tzw. operator overloading, to kod można zapisać jeszcze zwięźlej: architecture best of sel is begin q <= a when s = 8 else b; end; Z punktu widzenia syntezy fakt używania funkcji konwersji nie ma znaczenia, ponieważ nie wymaga syntezy dodatkowej logiki (ale dla symulacji ma!). Dobre narzędzia do syntezy (j.w.) pozwalają na napisanie kodu bez funkcji konwersji. W kodzie syntezowalnym należy używać typu std_logic_vector 14
Konwersje typów wsparcie bibliotek -------------------------------------------------------------------------- -- Copyright (c) 1990,1991,1992 by Synopsys, Inc. All rights reserved. -- -------------------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.all; package std_logic_arith is.. ----------------------- -- conversion operators ----------------------- function CONV_INTEGER(ARG: INTEGER) return INTEGER; function CONV_INTEGER(ARG: UNSIGNED) return INTEGER; function CONV_INTEGER(ARG: SIGNED) return INTEGER; function CONV_INTEGER(ARG: STD_ULOGIC) return SMALL_INT; function CONV_UNSIGNED(ARG: INTEGER; SIZE: INTEGER) return UNSIGNED; function CONV_UNSIGNED(ARG: UNSIGNED; SIZE: INTEGER) return UNSIGNED; function CONV_UNSIGNED(ARG: SIGNED; SIZE: INTEGER) return UNSIGNED; function CONV_UNSIGNED(ARG: STD_ULOGIC; SIZE: INTEGER) return UNSIGNED; function CONV_SIGNED(ARG: INTEGER; SIZE: INTEGER) return SIGNED; function CONV_SIGNED(ARG: UNSIGNED; SIZE: INTEGER) return SIGNED; function CONV_SIGNED(ARG: SIGNED; SIZE: INTEGER) return SIGNED; function CONV_SIGNED(ARG: STD_ULOGIC; SIZE: INTEGER) return SIGNED; function CONV_STD_LOGIC_VECTOR(ARG: INTEGER; SIZE: INTEGER) return STD_LOGIC_VECTOR; function CONV_STD_LOGIC_VECTOR(ARG: UNSIGNED; SIZE: INTEGER) return STD_LOGIC_VECTOR; function CONV_STD_LOGIC_VECTOR(ARG: SIGNED; SIZE: INTEGER) return STD_LOGIC_VECTOR; function CONV_STD_LOGIC_VECTOR(ARG: STD_ULOGIC; SIZE: INTEGER) return STD_LOGIC_VECTOR;... 15
Podtypy skalarne Są uściśleniem wcześniej zdefiniowanych typów przez zawężenie ich zakresu. Przydatne przy deklarowaniu większej liczby takich samych obiektów oraz dla utrzymania czytelności i zwięzłości kodu. Przykłady: subtype digit is integer range 0 to 9; variable msd, lsd: digit; jest równoważny: variable msd, lsd: integer range 0 to 9; type instr is (add, sub, mul, div, sta, stb, outa, xfr); subtype arith is instr range add to div; subtype pos is integer range 1 to 2147483647; subtype nano is time range 0 ns to 1 us; 16
Tablice Tablice składają się z elementów tego samego typu. Są one używane do opisu magistral, rejestrów i innych zbiorów elementów sprzętowych. Elementy tablic mogą być skalarami lub elementami złożonymi. (Nie można tworzyć np. tablic plików!!) Dostęp do poszczególnych elementów dzięki użyciu wskaźników/indeksów. Jedynymi predefiniowanymi typami tablicowymi są: string (package STANDARD) bit_vector (package STANDARD) std_logic_vector (package STD_LOGIC_1164) Użytkownik musi sam deklarować nowe typy tablic dla elementów real i integer. 17
Tablice deklaracja zakresu indeksów Sposób dostępu zależy od sposobu deklaracji. MSB (zawsze po lewej) Przykłady: variable c: bit_vector (0 to 3); variable d: bit_vector (3 downto 0); -- MOCNO PREFEROWANE c := 1010 ; d := c; c(0) c(1) c(2) c(3) 1 0 1 0 d(3) d(2) d(1) d(0) b(4 to 7) dowolny zakres c(4) indeks poza zakresem c(1.0) błędny typ indeksu 18
Tablice - przypisania Przykład: dla typu bit_vector: c := 1010 ; c := x A ; c := S & T & M & W; c := ( 1, 0, 1, 0 ); c := 10; stała typu bit_vector j.w., uwaga na długość! 4 połączone sygnały 1-bitowe 4-bitowy agregat niedozwolony Fragment tablicy (slice) Przypisania mogą zachodzić pomiędzy fragmentami wektorów. Przykłady: variable a: bit_vector (3 downto 0); variable c: bit_vector (8 downto 1); c(6 downto 3) := a; c(6 downto 3) a nie c(3 to 6) - kierunek indeksów musi być taki sam jak w deklaracji! 19
Zastosowanie tablic - rejestry 20
Agregaty Literał tablicowy może zawierać listę elementów w notacji pozycyjnej i / lub specyfikacyjnej, tworząc tzw. agregat. Składnia: [type_name ] ([choice =>] expression1 {,[others =>] expression2}) Przykłady: variable a,b: bit := 1 ; variable x,y: bit_vector (1 to 4); -- notacja pozycyjna x := bit_vector ( 1,a nand b, 1,a or b); -- notacja specyfikacyjna y := (1 => 1,4 => a or b,2 => a nand b,3 => 1 ); Notacja specyfikacyjna jest bardzo często wykorzystywana przy tworzeniu kodu przez narzędzia np w generatorach TestBench-y w instrukcji port map 21
Agregaty Przy użyciu notacji specyfikacyjnej, [choice =>] wskazuje na jeden lub kilka elementów. [choice =>] może zawierać wyrażenie (n.p.: (i mod 2) => ), wskazujące na jeden element lub zawierać zakres (n.p.: 3 to 5 => lub 7 downto 0 => ), wskazujący na sekwencję elementów. Notacja pozycyjna musi być użyta przed notacją specyfikacyjną. Przykład: variable b: bit; variable c: bit_vector (8 downto 1); c := bit_vector ( 1,b,5 downto 2 => 1,others => 0 ); Bardzo wygodne: Counter <= (others => 0 ); Data_Bus <= (others => Z ); 22
Deklaracja typu tablicowego Aby zadeklarować typ tablicowy należy wyspecyfikować: nazwę typu, typ elementów, liczbę indeksów, typ indeksów oraz zakres indeksów (opcjonalnie). Składnia: type name is array [index_constraint] of element_type; index_constraint: [range_spec] index_type range [range_spec] index_type range <> Przykłady: type word8 is array (1 to 8) of bit; type word8 is array (integer range 1 to 8) of bit; type word is array (integer range <>) of bit; type ram is array (1 to 8, 1 to 10) of bit; <> oznacza zakres nieograniczony (ang. box) 23
Deklaracja typu tablicowego Po deklaracji typu może on zostać użyty do deklaracji zmiennej lub sygnału. Przykład: variable data_bus: word8; variable register: word (1 to 10); Typ wyliczeniowy albo podtyp może również być użyty do oznaczenia zakresu zmienności indeksów. Przykłady: type instruction is (add, sub, mul, div, lda, sta, xfr); subtype arithmetic is instruction range add to div; subtype digit is integer range 1 to 9; type Ten_bit is array (digit) of bit; type Inst_flag is array (instruction) of digit; 24
Tablice wielowymiarowe Szczególnie użyteczne dla opisu pamięci RAM lub ROM. Przykład: type memory is array (0 to 7, 0 to 3) of bit; constant rom: memory := (( 0, 0, 0, 0 ), ( 0, 0, 0, 1 ), ( 0, 0, 1, 0 ), ( 0, 0, 1, 1 ), ( 0, 1, 0, 0 ), ( 0, 1, 0, 0 ), ( 0, 1, 1, 0 ), ( 0, 1, 0, 1 )); data_bit := rom(5,3); -- słowo 5, bit 3 25
Tablice tablic Przykład: type word is array (0 to 3) of bit; type memory is array (0 to 4) of word; variable addr, index: integer; variable data: word; constant rom_data: memory := (( 0, 0, 0, 0 ), ( 0, 0, 0, 1 ), ( 0, 0, 1, 0 ), ( 0, 1, 1, 1 ), ( 0, 1, 1, 1 )); data := rom_data(addr); rom_data(addr)(index) --dostęp do pojedynczego bitu 26
Przykłady modelowania pamięci 27
Rekordy Grupują obiekty różnych typów. Elementy rekordów mogą być skalarami lub typami złożonymi i są dostępne przez nazwę. Przykład: type two_digit is record sign: bit; msd: integer range 0 to 9; lsd: integer range 0 to 9; end record; process variable acntr, bcntr: two_digit; begin acntr.sign := 1 ; acntr.msd := 1; acntr.lsd := acntr.msd; bcntr := two_digit ( 0,3,6); end process; 28
Instrukcja alias Umożliwia wprowadzenie alternatywnej nazwy dla części obiektu, ułatwiającej dostęp. Przykład: signal count: bit_vector (1 to 9); alias sign: bit is count (1); alias msd: bit_vector (1 to 4) is count (2 to 5); alias lsd: bit_vector (1 to 4) is count (6 to 9); count := 1_1001_0000 ; sign := 1 ; msd := 1001 ; lsd := msd; 29
Predefiniowane typy tekstowe Należą do nich typy text oraz line. Używane są do operacji wejścia i wyjścia podczas symulacji. Występują w deklaracji file wraz z funkcjami odczytu, zapisu i pomocniczymi. STD library - TEXTIO Package: readline, read, writeline, write Przykład: readline (F: in text; L: out line); read (L: inout line; ITEM: integer); IEEE library STD_LOGIC_TEXTIO Package: read, write oread, owrite hread, hwrite Wbudowane: endfile(filename), endline(linename) 30
Predefiniowane typy tekstowe package STD_LOGIC_TEXTIO is -- Read and Write procedures for STD_ULOGIC and STD_ULOGIC_VECTOR procedure READ(L:inout LINE; VALUE:out STD_ULOGIC); procedure READ(L:inout LINE; VALUE:out STD_ULOGIC; GOOD: out BOOLEAN); procedure READ(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR); procedure READ(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR; GOOD: out BOOLEAN); procedure WRITE(L:inout LINE; VALUE:in STD_ULOGIC; JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0); procedure WRITE(L:inout LINE; VALUE:in STD_ULOGIC_VECTOR; JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0); -- Read and Write procedures for STD_LOGIC_VECTOR procedure READ(L:inout LINE; VALUE:out STD_LOGIC_VECTOR); procedure READ(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN); procedure WRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR; JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0); -- -- Read and Write procedures for Hex and Octal values. -- The values appear in the file as a series of characters -- between 0-F (Hex), or 0-7 (Octal) respectively. -- -- Hex procedure HREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR); procedure HREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR; GOOD: out BOOLEAN); -- itd... -- Octal 31
Predefiniowane typy tekstowe - przykład use STD.textio.all; library IEEE; use... use IEEE.std_logic_textio.all; entity adsp2191 is port(rd: out STD_LOGIC; wr: out STD_LOGIC; ms3: out STD_LOGIC; addr: out STD_LOGIC_VECTOR(15 downto 0); data: inout STD_LOGIC_VECTOR(15 downto 0); clkout: out STD_LOGIC); end adsp2191; architecture behavioral of adsp2191 is... file transfers: TEXT open READ_MODE is "adsp2191.txt"; file results: TEXT open WRITE_MODE is "simulation.txt";... 32
Predefiniowane typy tekstowe - przykład procedure read_cycle (...) is constant T_CSRS: time := H_clk_per/2-3ns; constant T_ARS: time := H_clk_per/2-3ns; constant T_RW: time := H_clk_per - 2ns + (WS * H_clk_per); constant T_RSA: time := H_clk_per/2-2ns; begin ms3 <= '0'; addr <= address; wait for T_ARS; rd <= '0'; wait for T_RW; rd <= '1'; data_read := data; wait for T_RSA; addr <= (others => 'Z'); ms3 <= '1'; end procedure read_cycle; 33
Predefiniowane typy tekstowe - przykład adsp2191.txt -> begin... READ_CODE:process begin N 0028 0000 #czekaj na inicjalizację W 4011 0001 #uruchom DCM R 4011 0001 #sprawdź DCM W 4001 01FF #ustaw długość akwizycji W 4002 00FF #ustaw liczbę akumulacji W 4000 0001 #ustaw 'Run' @ OneShot N 0035 0000 #czekaj n * 12.5ns W 4003 0000 #kasuj 'Int' W 4000 0000 #kasuj 'Run' R 0000 0200 #czytaj bufor danych while not (endfile(transfers)) loop -- end file checking readline(transfers,in_line); -- read line of a file read(in_line,code); -- read in operation code hread(in_line,address); -- read in address hread(in_line,data); -- read in data code_array(i) := code; -- put code in code table address_array(i) := address; -- put address in address table data_wr_array(i) := data; -- put data in data table i := i + 1; end loop; 34
Predefiniowane typy tekstowe - przykład... for index in 0 to max loop decode: case code_array(index) is when 'W' =>... write_cycle(...); -- write cycle timing model write(out_line, NOW, right, 12, ns); -- current simulation time write(out_line, code_array(index), right); -- current code write(out_line, address_array(index), right); -- current address write(out_line, data_wr_array(index), right); -- current data writeline(results,out_line); when R' =>...... simulation.txt ->... 6939 ns W 0100000000000000 0000000000000000 6964 ns N 6994.5 ns R 0000000000000000 0000000100000000 7025 ns R 0000000000000001 0000000100100000 7055.5 ns R 0000000000000010 0000000101000000 7086 ns R 0000000000000011 0000000101100000... 35
Predefiniowane typy tekstowe - przykład MultiShot_tim.awf 36
Deklaracje typów Deklaracja typu musi się pojawić się przed jego użyciem. Typ zadeklarowany w entity nie może być użyty w specyfikacji portów, ponieważ pojawia się ona przed deklaracjami typów. entity E is type_declaration begin... end E; architecture A of E is type_declaration begin... end A; P: process type_declaration begin... end process P; function F (...) is type_declaration begin... end F; package P is type_declaration end P; B: block type_declaration begin... end B; 37
Ciąg dalszy nastąpi...