Skocz do zawartości

IP core "FPU_Double" - problem z długościami magistral: 3 x 64 bity


Pomocna odpowiedź

Witam wszystkich,

chciałem zobaczyć jak wygląda wydajność obliczeń (4 podstawowe działania) dla liczb zmiennopozycyjnych Double dla popularnych układów FPGA. Znalazłem na stronie opencores.org IP core: FPU Double VHDL. Oto link do strony:

https://opencores.org/project/fpu_double

W zał1. dołączam pobrany kod źródłowy (VHDL) dla tego IP core.

Okazało się, że projekt zajmuje za dużo zasobów dla płytek "Elbert V2" oraz "Mimas V2",
dlatego postanowiłem go zaimplementować za pomocą płytki FPGA "Digilent Cmod A7-35T ", opartej na FPGA Artix-7. Oto link do tej płytki:

https://kamami.pl/zestawy-uruchomieniowe/562401-digilent-cmod-a7-35t-modul-uruchomieniowy-z-fpga-artix-7-410-328-35.html

Użyty układ FPGA: XC7A35T-1CPG236C zawiera około 20800 LUT (w porównaniu do Elbert'a 1400 LUT). Projekt "FPU Double zajmuje sporo zasobów, jak widać na zrzucie ekranu poniżej:

I tutaj napotkałem problem, oto główne entity projektu:

  ENTITY fpu_double IS

  PORT( 
     clk, rst, enable : IN     std_logic;
     rmode : IN     std_logic_vector (1 DOWNTO 0);
     fpu_op : IN     std_logic_vector (2 DOWNTO 0);
     opa, opb : IN     std_logic_vector (63 DOWNTO 0);
     out_fp: OUT    std_logic_vector (63 DOWNTO 0);
     ready, underflow, overflow, inexact : OUT    std_logic;
     exception, invalid : OUT    std_logic
  );

  END fpu_double;

Jak widać FPU ma dwie wejściowe magistrale o długości 64 bit każda i jedną wyjściową - także 64-bitową.

Jest to problem, który napotykam już w którymś z kolei projekcie o kodzie dostępnym w sieci: długości magistral układu mają liczbę pinów I/O przekraczającą znacznie ilości pinów I/O danego zestawu FPGA (a często także samego układu scalonego FPGA użytego w danym zestawie).

Płytka ""Digilent Cmod A7-35T" posiada łącznie 48 pinów I/O, więc postanowiłem nieco skrócić magistrale układu. Każdą z dwóch magistral wejściowych skróciłem do 16-tu bitów

- liczby double w tym projekcie FPU mają długość 64 bity, więc jej wprowadzenie można zrobić w czterech porcjach po 16 bitów. Na każdą z magistral wejściowych podłączyłem demultiplekser (z 16 na 64 bit). Oprócz danych wejściowych potrzebny jest jeszcze adres 16-to bitowej części i sygnał "strobe", który zboczem narastającym przepisuje 16-bitową porcję danych na właściwe miejsce w 64 bitowym rejestrze wyjściowym.

Oto kod entity Demux8To64:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_misc.all;

entity Demux8To64 is
   Port ( in16bit : in STD_LOGIC_VECTOR (15 downto 0);
          sel    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 16-bit input (4 parts)
          out64bit : out STD_LOGIC_VECTOR (63 downto 0);
          strobe : in STD_LOGIC;   -- Active rising edge
          resetL : in STD_LOGIC;   -- Active LOW 
          CLK    : in STD_LOGIC    -- clock
       );
end Demux8To64;

architecture Behavioral of Demux8To64 is

begin
 SEQ: process (CLK, resetL, strobe)
 begin
       if (resetL = '0') then
            out64bit <= (others => '0');    
       elsif (strobe' event and strobe = '1') then
             case sel is				 
             when "00" => 
                out64bit(63 downto 48) <= in16bit; --Most significant 16-bit part           
             when "01" => 
                out64bit(47 downto 32) <= in16bit;
             when "10" => 
                out64bit(31 downto 16) <= in16bit;             
             when "11" => 
                out64bit(15 downto 0) <= in16bit;           
             when others => null;
             end case;
       else
         NULL;  --Do nothing
       end if;
end process; -------------------------------------

end Behavioral;

Na wyjściu "FPUDouble" (wynik obliczeń liczba 64 bitowa) umieściłem multiplekser wyjściowy 64 bity na 16 bitów. Oto kod entity Mux64To16:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Mux64To16 is
Port ( 
          in64bit : in STD_LOGIC_VECTOR (63 downto 0);
          sel    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 16-bit input (4 parts)
          out16bit : out STD_LOGIC_VECTOR (15 downto 0);
          strobe : in STD_LOGIC;   -- Active rising edge
          resetL : in STD_LOGIC;   -- Active LOW 
          CLK    : in STD_LOGIC    -- clock
      );
end Mux64To16;

architecture Behavioral of Mux64To16 is

begin

 SEQ: process (CLK, resetL, strobe)
 begin
     if (resetL = '0') then
          out16bit <= (others => '0');    
     elsif (strobe' event and strobe = '1') then
           case sel is                 
           when "00" => 
              out16bit <= in64bit(63 downto 48); --Most significant 16-bit part           
           when "01" => 
              out16bit <= in64bit(47 downto 32);
           when "10" => 
              out16bit <= in64bit(31 downto 16);             
           when "11" => 
               out16bit <= in64bit(15 downto 0);           
           when others => null;
           end case;
     else
       NULL;  --Do nothing
     end if;
 end process; -------------------------------------

end Behavioral;

Dodałem nowy plik źródłowy (VHDL) entity fpu_top, który teraz jest "top entity" projektu (oryginalnie było nim entity fpu_double. oto kod entity fpu_top:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_misc.all;
library work;      
use work.comppack.all;
use work.fpupack.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity fpu_top is
  PORT( 
  in16bitA : in STD_LOGIC_VECTOR (15 downto 0);
  selA    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 8-bit input (4 parts)
  strobeA : in STD_LOGIC;   -- Active rising edge
  resetLA : in STD_LOGIC;   -- Active LOW 
  ----
  in16bitB : in STD_LOGIC_VECTOR (15 downto 0);
  selB    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 8-bit input (4 parts)
  strobeB : in STD_LOGIC;   -- Active rising edge
  resetLB : in STD_LOGIC;   -- Active LOW
  ----
  pclk, prst, penable : IN     std_logic;
  prmode : IN     std_logic_vector (1 DOWNTO 0);
  pfpu_op : IN     std_logic_vector (2 DOWNTO 0);
  pout_fp: OUT    std_logic_vector (15 DOWNTO 0); -- 16 bit out after Multiplexer
  pready, punderflow, poverflow, pinexact : OUT    std_logic;
  pexception, pinvalid : OUT    std_logic;
  --
  selMux    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 8-bit input (4 parts)
  strobeMux : in STD_LOGIC;   -- Active rising edge
  resetLMux : in STD_LOGIC   -- Active LOW
  );
end fpu_top;

architecture Behavioral of fpu_top is

 component Demux8To64 is
   Port ( in16bit : in STD_LOGIC_VECTOR (15 downto 0);
          sel    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 16-bit input (4 parts)
          out64bit : out STD_LOGIC_VECTOR (63 downto 0);
          strobe : in STD_LOGIC;   -- Active rising edge
          resetL : in STD_LOGIC;   -- Active LOW 
          CLK    : in STD_LOGIC    -- clock
       );
 end component;

 component fpu_double IS  
    PORT( 
       clk, rst, enable : IN     std_logic;
       rmode : IN     std_logic_vector (1 DOWNTO 0);
       fpu_op : IN     std_logic_vector (2 DOWNTO 0);
       opa, opb : IN     std_logic_vector (63 DOWNTO 0);
       out_fp: OUT    std_logic_vector (63 DOWNTO 0);
       ready, underflow, overflow, inexact : OUT    std_logic;
       exception, invalid : OUT    std_logic
    );
 end component;

  component Mux64To16 is
    Port ( 
            in64bit : in STD_LOGIC_VECTOR (63 downto 0);
            sel    :  in STD_LOGIC_VECTOR (1 downto 0); --Adress of 16-bit input (4 parts)
            out16bit : out STD_LOGIC_VECTOR (15 downto 0);
            strobe : in STD_LOGIC;   -- Active rising edge
            resetL : in STD_LOGIC;   -- Active LOW 
            CLK    : in STD_LOGIC    -- clock
        );
  end component;

  signal outDemuxA : STD_LOGIC_VECTOR (63 downto 0);
  signal outDemuxB : STD_LOGIC_VECTOR (63 downto 0);
  signal fpuOut64bit : STD_LOGIC_VECTOR (63 downto 0);

begin
 DmuxA : Demux8To64 port map (in16bitA, selA, outDemuxA, strobeA, resetLA, pclk); 
 DmuxB : Demux8To64 port map (in16bitB, selB, outDemuxB, strobeB, resetLB, pclk);

 FPUD : fpu_double port map (pclk,
           prst,
           penable, 
           prmode,
           pfpu_op,
           outDemuxA,
           outDemuxB,
           fpuOut64bit,
           pready,
           punderflow,
           poverflow, 
           pinexact, 
           pexception,
           pinvalid 
           );

MuxOut: Mux64To16 port map (
             fpuOut64bit, 
             selMux, 
             pout_fp,
             strobeMux,
             resetLMux,
             pclk 
          );

end Behavioral;

Tutaj oryginalny kod entity początek fpu_double:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_misc.all;
library work;	  
use work.comppack.all;
use work.fpupack.all;

ENTITY fpu_double IS

  PORT( 
     clk, rst, enable : IN     std_logic;
     rmode : IN     std_logic_vector (1 DOWNTO 0);
     fpu_op : IN     std_logic_vector (2 DOWNTO 0);
     opa, opb : IN     std_logic_vector (63 DOWNTO 0);
     out_fp: OUT    std_logic_vector (63 DOWNTO 0);
     ready, underflow, overflow, inexact : OUT    std_logic;
     exception, invalid : OUT    std_logic
  );

END fpu_double;

-- FPU Operations (fpu_op):
--========================
--	0 = add
--	1 = sub
--	2 = mul
--	3 = div

--Rounding Modes (rmode):
--=======================
--	0 = round_nearest_even
--	1 = round_to_zero
--	2 = round_up
--	3 = round_down  

architecture rtl of fpu_double is
...

Niestety źle policzyłem i nadal zabrakło mi pinów I/O - patrz zrzut ekranu poniżej:

Tzn. synteza przechodzi bez błędów a są błędy w fazie implementacji bo brakuje pinów I/O.

To co mogę zrobić to np. zmienić jeszcze długość magistral IN/OUT do 8 bitów i wprowadzać/wyprowadzać liczby 64-bitowe w ośmiu porcjach po 8-bitów. Drugą opcją jest zastosowanie rejestrów przesuwnych gdzie dane są wprowadzane szeregowo do rejestru równoległego 64 bitowego i na wyjściu wyprowadzane szeregowo.

I stąd moje bardziej ogólne pytanie: jakiego rozwiązania używać w przypadku "za długich" magistral w projekcie FPGA?

1) Czy używać wprowadzania/wyprowadzania danych szeregowo (z głównego entity projektu)?

2) Czy używać krótszych magistral np. 1-bajtowych i wprowadzać/wyprowadzać dane porcjami zależnymi od długości głównej magistrali?

To o czym jeszcze pomyślałem to zrobić gotowe sprzętowe moduły na CPLD (lub mniejszych FPGA), skracające długości magistral (np. konfigurowalne za pomocą "DIPswitch) i podłączalne do zestawów FPGA (łącznie z własnym drukiem płytki).

To co przychodzi mi na myśl w przypadku mojej płytki "Digilent Cmod A7-35T" to użycie

512 kB pamięci SRAM z 8-bitową szyną i czasem dostępu 8 ns

Będę odczytywał dane do obliczeń z tej pamięći SRAM (8 bajtów) i zapisywał wynik także w postaci 8 bajtów w innej lokalizacji RAM.

Podobny problem napotkałem przy realizacji Soft-CPU "Microblaze" Xilinx'a.

Jakieś przemyślenia, czy podpowiedzi ? Będę wdzięczny za każdy komentarz 😃

W drugim załączniku zamieszczam projekt Vivado "FPUDouble" - synteza kończy się pomyślnie, implementacja nie (z powodu braku pinów I/O).

Pozdrawiam

fpu_double_latest.tar.gz

FPUDouble02.zip

Link do komentarza
Share on other sites

FlyingDutch, wyprowadzanie 64-bitowej magistrali z FPGA oczywiście jest możliwe, ale to chyba trochę bez sensu. Nawet gdyby wystarczyło pinów, to co byś z tą magistralą zrobił? Podłączył 64 przełączniki i diody, żeby wprowadzać dane i odczytywać wyniki?

Tak szeroka magistrala ma sens wewnątrz układu - a jak sam już odkryłeś na zewnątrz lepiej używać czegoś mniej wymagającego - magistrali 8-bitowej, czy nawet łącza szeregowego.

Ja ostatnio doszedłem do podobnego punktu w moich "studiach" nad FPGA - czyli do zagadki skad brać dane i co z nimi robić. Dlatego kombinuję z procesorami w FPGA - wtedy nie muszę wystawiać i ręcznie dekodować pinów, magistralę można zrealizować wewnątrz układu, a za generowanie, odbieranie, czy przesyłanie danych może odpowiadać procesor.

Link do komentarza
Share on other sites

FlyingDutch,

Tak szeroka magistrala ma sens wewnątrz układu - a jak sam już odkryłeś na zewnątrz lepiej używać czegoś mniej wymagającego - magistrali 8-bitowej, czy nawet łącza szeregowego.

Ja ostatnio doszedłem do podobnego punktu w moich "studiach" nad FPGA - czyli do zagadki skad brać dane i co z nimi robić. Dlatego kombinuję z procesorami w FPGA - wtedy nie muszę wystawiać i ręcznie dekodować pinów, magistralę można zrealizować wewnątrz układu, a za generowanie, odbieranie, czy przesyłanie danych może odpowiadać procesor.

Cześć Elvis,

widzę, że doszliśmy do podobnych wniosków 😉 Na pierwszy rzut spróbuję użyć pamięci SRAM na mojej płytce Cmod-A7 (interfejs pamięci SRAM jest za-mapowany na piny FPGA Artix-7).

Poeksperymentuje z soft-CPU "Microblaze" i/lub NIOS (na płytce Maximator) i tym IP core "FPUDouble".

Tak z ciekawości znasz może jakieś zestawienie dla ARM Cortex-M7 z STM32Nucleo-F7 ile taktów zegara zajmują 4 działania arytmetyczne z wykorzystaniem FPU tego procka?

Pozdrawiam

Link do komentarza
Share on other sites

Jak będziesz miał jakieś rezultaty z soft-CPU to pisz 🙂 Ja próbuję się zabrać, ale jakoś ciągle mało czasu. Więc poza tutorialem - jak wgrać Nios-a do Maximatora na razie niewiele zrobiłem.

A jak chodzi o Cortex-M7 to nie miałem jeszcze czasu się pobawić niestety. Zatrzymałem się na M4, albo raczej przeskoczyłem na A5 😉

Dla M4 informacja jest np. tutaj: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/BEHJADED.html

O M7 widzę, że piszą jakie są instrukcje, ale już nie bardzo ile taktów zajmują: http://infocenter.arm.com/help/topic/com.arm.doc.dui0646a/CHDHHAJF.html

Ale ogólnie strony ARM-a to chyba najlepszy kierunek poszukiwań. Inna opcja to zrobienie testów - to chyba nawet lepsze, bo można się więcej nauczyć.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Jak będziesz miał jakieś rezultaty z soft-CPU to pisz 🙂 Ja próbuję się zabrać, ale jakoś ciągle mało czasu. Więc poza tutorialem - jak wgrać Nios-a do Maximatora na razie niewiele zrobiłem.

A jak chodzi o Cortex-M7 to nie miałem jeszcze czasu się pobawić niestety. Zatrzymałem się na M4, albo raczej przeskoczyłem na A5 😉

Dla M4 informacja jest np. tutaj: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/BEHJADED.html

O M7 widzę, że piszą jakie są instrukcje, ale już nie bardzo ile taktów zajmują: http://infocenter.arm.com/help/topic/com.arm.doc.dui0646a/CHDHHAJF.html

Ale ogólnie strony ARM-a to chyba najlepszy kierunek poszukiwań. Inna opcja to zrobienie testów - to chyba nawet lepsze, bo można się więcej nauczyć.

Cześć Elvis,

z podanych linków wynika, że FPU w ARM Cortex-M4 jest szybsze niż to z IP core. Np. czas dzielenia dla ArM'a M4 to 14 cykli zegara a dla FPU FPGA 71 cykli. w Microblaze FPU można dodać jako opcję, nie wiem jak to jest z NIOS.

Jak będę miał jakieś wyniki to napiszę.

Update: udało mi się w pełni zsyntetyzować "FPUDouble" (implementacja przeszła), ale pojawił się nowy problem: Vivado sygnalizuje przekroczenie mocy układu FPGA (wydzielana moc około 33 W, a dopuszczalna 20 W - temperatura układu FPGA przekracza 125 stopni).

Patrz zrzut ekranu poniżej:

Poprawiłem plik XDC (user constraints) - problem powodowały sygnały: strobeA i strobeB (nie były synchronizowane z głównym zegarem). Rozwiązaniem (brzydkim i chwilowym były klauzule:

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets strobeA]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets strobeB]

w pliku user constraints. Poprawię to niedługo (zsynchronizuję te sygnały z zegarem), ale to trochę później. Teraz wszystkie trzy magistrale z entity fpu_top mają po 8-bitów, dane są wprowadzane i wyprowadzane w ośmiu porcjach.

Teraz implementacja zajmuje ułamek wata, patrz obrazek:

W załączniku syntetyzujący się projekt Vivado (poprawiony).

BTW: znalazłem fajny konwerter liczby Double do 64 bitowej postaci (IEEE754 Double):

http://www.binaryconvert.com/convert_double.html

Pozdrawiam

FPUDouble02P.zip

Link do komentarza
Share on other sites

Cześć,

uruchomiłem implementację "FPU Double" w Vivado z zegarem 100 MHz (okres: 10 ns) i wygląda na to, że projekt może pracować bez problemu z taką częstotliwością (ograniczenia czasowe i wydzielana moc są w prawidłowym zakresie).

Czasy poszczególnych operacji arytmetycznych są według autora następujące:

Each operation takes the following amount of clock cycles to complete:

1. addition : 20 clock cycles - 200ns

2. subtraction: 21 clock cycles - 210 ns

3. multiplication: 24 clock cycles - 240 ns

4. division: 71 clock cycles - 710 ns

---------------------------------------------------------------

Dla zegara 250 MHz implementacja kończy się błędem - nie spełnione ograniczenia czasowe.

Dla zegara 142 MHz (okres: 7ns), też nie przechodzi z powodu ograniczeń czasowych.

A jednak spróbowałem dla zegara 111 MHz (okres: 9ns) i dla tej częstotliwości układ jeszcze powinien działać OK 😃

Nie chce mi się już więcej próbować, bo każda faza syntezy i implementacji trwa u mnie długo. Wygląda jednak na to że na płytce FPGA CmodA7-35T (Artix-7) projekt jednostki zmiennoprzecinkowej może pracować z max. częstotliwością zegara niewiele większą niż 100 MHz.

Następnym etapem prac nad tym projektem będzie ulepszenie design'u: zsynchronizowanie trzech sygnałów strobe z głównym zegarem projektu (dotychczas były asynchroniczne).

Następnie wykonanie symulacji układu.

Potem wykorzystanie pamięci SRAM z tej płytki FPGA do zapisu danych do obliczeń i wyników (liczby 64-bitowe Double).

Na koniec uruchomienie projektu na zestawie FPGA i pomierzenie rzeczywistych czasów wykonania obliczeń.

Wklejam, jeszcze link, który był pomocny - ustalenie max.częstotliwości pracy układu:

http://billauer.co.il/blog/2017/01/vivado-minimal-period-timing/

Niestety w Vivado, jest to dużo gorsze niż w ISE, bo trzeba uruchamiać syntezę i implementację z różnymi ograniczeniami czasowymi i widzimy, czy implementacja przechodzi bez błędów lub nie.

Pozdrawiam

Link do komentarza
Share on other sites

Cześć,

zsynchronizowałem dziś z głównym zegarem płytki FPGA trzy sygnały strobe używane w demultiplekserach i multiplekserze wyjściowym do przepisania porcji danych wejściowych/wyjściowych. Zrobiłem to za pomocą rejestru (można to też osiągnąć za pomocą zwyłkego przerzutnika typu D: zegar podajemy na wejście zegarowe przerzutnika, na wejście D sygnał synchronizowany, a z wyjścia prz. Q bierzemy sygnał zsynchronizowany z zegarem).

Utworzyłem entity SyncToClock identyczne jak w bibliotece XESS:

https://github.com/xesscorp/VHDL_Lib/blob/master/SyncToClk.vhd

Oto ten dodany plik:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity SyncToClock is
 generic (
   NUM_SYNC_STAGES_G : natural := 2
   );
 port (
   clk_i      : in  std_logic;         -- Clock for the domain being entered.
   unsynced_i : in  std_logic;         -- Signal that is entering domain.
   synced_o   : out std_logic          -- Signal sync'ed to clock domain
   );
end entity;

architecture arch of SyncToClock is
 -- This is the sync'ing shift register.  The index indicates the number of clocked flip-flops the incoming signal
 -- has passed through, so sync_r(1) is one clk_i cycle stage, sync_r(2) is two cycles, etc.
 signal sync_r : std_logic_vector(NUM_SYNC_STAGES_G downto 1);

begin
 process(clk_i)
 begin
   if rising_edge(clk_i) then
     -- Shift the unsync'ed signal into one end of the sync'ing register.
     sync_r <= sync_r(NUM_SYNC_STAGES_G-1 downto 1) & unsynced_i;
   end if;
 end process;
 -- Output the sync'ed signal from the other end of the shift register.
 synced_o <= sync_r(NUM_SYNC_STAGES_G);

end architecture;

Zmieniłem kod głównego projektu - dodałem trzy komponenty SyncToClock w celu synchronizacji sygnałów strobe. Tak teraz wygląda główne entity projektu:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_misc.all;
library work;      
use work.comppack.all;
use work.fpupack.all;

entity fpu_top is
  PORT( 
  in8bitA : in STD_LOGIC_VECTOR (7 downto 0);
  selA    :  in STD_LOGIC_VECTOR (2 downto 0); --Adress of 8-bit input (4 parts)
  strobeA : in STD_LOGIC;   -- Active rising edge
  resetLA : in STD_LOGIC;   -- Active LOW 
  ----
  in8bitB : in STD_LOGIC_VECTOR (7 downto 0);
  selB    :  in STD_LOGIC_VECTOR (2 downto 0); --Adress of 8-bit input (4 parts)
  strobeB : in STD_LOGIC;   -- Active rising edge
  resetLB : in STD_LOGIC;   -- Active LOW
  ----
  pclk, prst, penable : IN     std_logic;
  prmode : IN     std_logic_vector (1 DOWNTO 0);
  pfpu_op : IN     std_logic_vector (2 DOWNTO 0);
  pout_fp: OUT    std_logic_vector (7 DOWNTO 0); -- 8 bit out after Multiplexer
  pready, punderflow, poverflow, pinexact : OUT    std_logic;
  pexception, pinvalid : OUT    std_logic;
  --
  selMux    :  in STD_LOGIC_VECTOR (2 downto 0); --Adress of 8-bit input (4 parts)
  strobeMux : in STD_LOGIC;   -- Active rising edge
  resetLMux : in STD_LOGIC   -- Active LOW
  );
end fpu_top;

architecture Behavioral of fpu_top is

 component SyncToClock is
   generic (
      NUM_SYNC_STAGES_G : natural := 2
     );
   port (
     clk_i      : in  std_logic;         -- Clock for the domain being entered.
     unsynced_i : in  std_logic;         -- Signal that is entering domain.
     synced_o   : out std_logic          -- Signal sync'ed to clock domain
    );
 end component;

 component Demux8To64 is
   Port ( in8bit : in STD_LOGIC_VECTOR (7 downto 0);
          sel    :  in STD_LOGIC_VECTOR (2 downto 0); --Adress of 16-bit input (4 parts)
          out64bit : out STD_LOGIC_VECTOR (63 downto 0);
          strobe : in STD_LOGIC;   -- Active rising edge
          resetL : in STD_LOGIC;   -- Active LOW 
          CLK    : in STD_LOGIC    -- clock
       );
 end component;

 component fpu_double IS  
    PORT( 
       clk, rst, enable : IN     std_logic;
       rmode : IN     std_logic_vector (1 DOWNTO 0);
       fpu_op : IN     std_logic_vector (2 DOWNTO 0);
       opa, opb : IN     std_logic_vector (63 DOWNTO 0);
       out_fp: OUT    std_logic_vector (63 DOWNTO 0);
       ready, underflow, overflow, inexact : OUT    std_logic;
       exception, invalid : OUT    std_logic
    );
 end component;

  component Mux64To16 is
    Port ( 
            in64bit : in STD_LOGIC_VECTOR (63 downto 0);
            sel    :  in STD_LOGIC_VECTOR (2 downto 0); --Adress of 16-bit input (4 parts)
            out8bit : out STD_LOGIC_VECTOR (7 downto 0);
            strobe : in STD_LOGIC;   -- Active rising edge
            resetL : in STD_LOGIC;   -- Active LOW 
            CLK    : in STD_LOGIC    -- clock
        );
  end component;

  signal outDemuxA : STD_LOGIC_VECTOR (63 downto 0);
  signal outDemuxB : STD_LOGIC_VECTOR (63 downto 0);
  signal fpuOut64bit : STD_LOGIC_VECTOR (63 downto 0);
  signal strobeASync :  STD_LOGIC;
  signal strobeBSync :  STD_LOGIC;
  signal strobeMuxSync :  STD_LOGIC; 

begin
 SyncCLK1 : SyncToClock port map (pclk, strobeA, strobeASync);
 SyncCLK2 : SyncToClock port map (pclk, strobeB, strobeBSync);
 SyncCLK3 : SyncToClock port map (pclk, strobeMux, strobeMuxSync); 

 DmuxA : Demux8To64 port map (in8bitA, selA, outDemuxA, strobeASync, resetLA, pclk); 
 DmuxB : Demux8To64 port map (in8bitB, selB, outDemuxB, strobeBSync, resetLB, pclk);

 FPUD : fpu_double port map (pclk,
           prst,
           penable, 
           prmode,
           pfpu_op,
           outDemuxA,
           outDemuxB,
           fpuOut64bit,
           pready,
           punderflow,
           poverflow, 
           pinexact, 
           pexception,
           pinvalid 
           );

MuxOut: Mux64To16 port map (
             fpuOut64bit, 
             selMux, 
             pout_fp,
             strobeMuxSync,
             resetLMux,
             pclk 
          );

end Behavioral;

Mogłem teraz wyrzucić klauzule do obejścia błędów implementacji (złe przekazywanie sygnału zegarowego pomiędzy komponentami) z pliku user constraints:

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets strobeA]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets strobeB]

i nie ma teraz w fazie implementacji żadnych błędów, czy ostrzeżeń.

Cały projekt syntetyzuje się poprawnie. W zał. poprawiony projekt Vivado 2018.1.

FPUDouble02C.zip

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.