Na kursach z projektowania logicznego wszyscy dowiedzieliśmy się, że można zminimalizować funkcję logiczną, na przykład za pomocą mapy Karnaugh lub algorytmu Quine – McCluskey . Dowiedzieliśmy się również, że wartości „Don't Care” zwiększają potencjał minimalizacji.
Na przykład weź plik rejestru. write_address
I write_data
sygnały naprawdę nie ma znaczenia, gdy write_enable
sygnał jest '0'
. Dlatego należy im przypisać wartość „Don't Care”, aby umożliwić więcej optymalizacji w logice napędzającej te sygnały (tj. Nie w samym pliku rejestru).
Jaki jest prawidłowy sposób określenia takich wartości „Don't Care” w VHDL, aby pozwolić narzędziu syntezującemu więcej miejsca na możliwe optymalizacje?
Do tej pory znalazłem następujące rzeczy, które mogą być odpowiednie. Ale nie jestem pewien, jakie są zalety i wady każdego podejścia:
- Po prostu nie przypisuje sygnału. Wygląda na to, że może działać. Okazało się jednak, że nie działa, gdy chcesz zdefiniować pewnego rodzaju „stałą nic nie rób”
record
, ponieważ stałe zapisu muszą być w pełni określone (przynajmniej tak mówi mi Modelsim). std_logic_1164
Pakiet definiuje wartość'-' -- Don't care
dlastd_ulogic
. Wygląda na to, że jest to semantycznie poprawny wybór wyraźnego „nie przejmuj się”, ale nigdy nie widziałem, aby był używany nigdzie (z wyjątkiem niepowiązanychcase?
konstrukcji VHDL-2008 ).- Modeleim używa tej wartości
'X'
do wyświetlania niezdefiniowanych sygnałów. Nie jestem jednak pewien, czy narzędzia do syntezy rozumieją jawne'X'
przypisanie jako „nie przejmuj się”.
Oto uproszczony fragment kodu dla wyjaśnienia, w którym zainicjowałem sygnały „nie przejmuj się” '-'
.
Jak widać, sygnał control.reg_write_address
może mieć 3 różne wartości: "----"
, instruction(11 downto 8);
i instruction(3 downto 0);
. Teraz spodziewam się, że zostanie to zsyntetyzowane do multipleksera 2-wejściowego, jeśli '-'
zostanie zinterpretowane jako „nie obchodzi”. Gdybym zainicjował sygnał (others => '0')
zamiast '-'
, narzędzie musiałoby wygenerować 3-wejściowy multiplekser.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package mytypes is
type control_signals_t is record
write_enable : std_logic;
write_address : std_ulogic_vector(3 downto 0);
read_address : std_ulogic_vector(3 downto 0);
end record;
-- All members of this constant must be fully specified.
-- So it's not possible to simply not assign a value.
constant CONTROL_NOP : control_signals_t := (
write_enable => '0',
write_address => (others => '-'),
read_address => (others => '-')
);
end package;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library cfx;
use cfx.mytypes.all;
entity control_unit is
port(
instruction : in std_ulogic_vector(15 downto 0);
write_data : out std_ulogic_vector(15 downto 0);
ctrl : out control_signals_t
);
end entity;
architecture rtl of control_unit is
begin
decode_instruction : process(instruction) is
begin
-- Set sensible default values that do nothing.
-- Especially all "write_enable" signals should be '0'.
-- Everything else is mostly irrelevant (don't care).
ctrl <= CONTROL_NOP;
write_data <= (others => '-');
if instruction(15 downto 12) = "1100" then
-- Load 8 bit of data into the register file
ctrl.write_enable <= '1';
write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16));
ctrl.write_address <= instruction(11 downto 8);
elsif instruction(15 downto 8) = "11111001" then
-- Load 4 bit of data into the register file
write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16));
ctrl.write_address <= instruction(3 downto 0);
elsif instruction(15 downto 8) = "10110101" then
-- Read from the register file. Don't use the write signals at all.
ctrl.read_address <= instruction(3 downto 0);
end if;
end process;
end architecture;
źródło
write_address
iwrite_data
? Jakiej optymalizacji oczekujesz?Odpowiedzi:
Zostawię to ekspertowi LRM, aby udzielił bardziej szczegółowej odpowiedzi, ale w skrócie, twoje podejście powinno być prawidłowe - przeprowadziłem szybki test z najnowszą wersją Quartusa i działa
'-'
tak, jak powinien - wygenerowana logika jest zmniejszona zgodnie z oczekiwaniami, gdy wyjście jest domyślnie ustawione na'-'
('X'
działa też, nawiasem mówiąc,). Więcej na temat wymienionych przez ciebie podejść:Nie przypisanie sygnału nie jest tak naprawdę opcją dla twojego przykładu, oczywiście, jeśli nie chcesz zatrzasków. Jeśli jest to proces taktowany, nieco lepiej, ale nadal dostaniesz możliwości tam, gdzie ich nie potrzebujesz. Może brakuje mi twojej intencji tutaj.
'-'
, jak wcześniej wspomniano, jest prawdopodobnie najlepszą opcją, zarówno ze względów semantycznych, jak i praktycznych.Zależy od tego, co rozumiesz przez „niezdefiniowany”.
'X'
jest technicznie „nieznany”.'U'
jest dla niezainicjowanych sygnałów, które ModelSim wyświetla jak"X"
dla reprezentacji szesnastkowych.'X'
wydaje się jednak działać, jak zauważyłem powyżej.Inną alternatywą byłoby samodzielne przeprowadzenie optymalizacji i jednoznaczne usunięcie jednego przypadku z testu:
Ma to jednak znaczące wady (głównie związane z klarownością kodu) i prawdopodobnie wybrałbym bardziej idealne rozwiązanie.
Nawiasem mówiąc,
'-'
jest również powszechnie używany zstd_match()
, którego rozważę użycie do dekodowania, np .:Chociaż w tym momencie prawdopodobnie lepiej po prostu użyć
case?
.źródło
Krótko mówiąc: jest to legalny VHDL i zwykle jest obsługiwany przez narzędzia do syntezy.
Jednak rzadko się zdarza, że się go używa. Naprawdę nie wiem dlaczego. Twój kod wydaje mi się dobrym przykładem tego, kiedy jego użycie byłoby sensowne.
Jest jednak jedna wada, o której należy pamiętać: podczas syntezy funkcje sterujące wyjściami, w których nie ma znaczenia, mogą różnić się między przebiegami syntezy. To sprawia, że synteza jest mniej deterministyczna. Jeśli wyjścia, które zostały zdefiniowane jako nieistotne, są używane (przez pomyłkę), może to utrudnić znalezienie błędu.
Wsparcie narzędziowe
Przynajmniej następujące narzędzia zaakceptują „nie obchodzi mnie” i skorzystają z możliwości optymalizacji:
Xilinx i Altera będzie traktować
'-'
i'X'
jak nie obchodzi, Synplify będzie traktować tych, a ponadto'U'
i'W'
(słaby), a nie obchodzi.źródło
if signal = '1' then a; else b; end if;
. Niestetysignal
nie było1
lub0
ale-
. Tak więc w symulacjielse
gałąź została wykonana, ale w sprzęcie-
okazało się, że, a1
więc wykonano prawdziwą gałąź ...'U'
, które są powszechne na początku symulacji, które zostały użyte, prowadząc do wykonaniaelse
bloku kodu. Byłoby wspaniale, gdyby można było w jakiś sposób wprowadzić warunki warunkowe do propagowania'U'
s, podobnie jak zachowanie współbieżnych wyrażeń boolowskich.if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if;
. Dodałem następujące informacje do wszystkich rejestrów i pamięci:assert not is_X(write_enable) report "we=" & str(A_write_enable) severity ERROR;
iif write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;
. Plus to samo dlawrite_data
. Razem powinno to złapać prawie wszystkie z tych błędów.