Глава 6. Примеры проектирования цифровых устройств с использованием языков описания аппаратуры VHDL и Verilog. 6.1. Общие сведения Языки описания аппаратуры VHDL и Verilog HDL представляют собой языки высокого уровня (в отличие от AHDL и ABEL HDL) и предназначены для описания цифровых схем и их элементов в первую очередь на поведенческом уровне. Эти языки поддерживают различные уровни абстракции проекта, включая поддержку особенностей архитектуры ПЛИС, под которую выполняется проект (architecture-specific design). Использование языков описания аппаратуры высокого уровня позволяет проектировать цифровые системы, обладающие высокой мобильностью, то есть возможностью переносимости на другую элементную базу. Для создания законченного проекта необходимо только лишь произвести компиляцию в соответствующей системе в элементную базу выбранного производителя. Но в этом и основной недостаток языков высокого уровня – недостаточный учет специфических архитектурных особенностей используемой элементной базы (specific target technology). В этом разделе мы попытаемся рассмотреть некоторые примеры использования языков описания аппаратуры для создания проектов различного уровня сложности. При проведении синтеза логической структуры ПЛИС с использованием языков описания аппаратуры (HDL synthesis-based design flow) различают четыре основных стадии проектирования: - Создание и функциональная верификация проекта; - Реализация проекта в САПР ПЛИС; - Программирование ПЛИС - Верификация всей системы При функциональной верификации проекта ввод описания проекта осуществляется на регистровом уровне (RTL-level) в поведенческой области (behavioral). После ввода описания проекта поведенческое (функциональное) моделирование (верификация) позволяют оценить степень правильности функционирования алгоритма. После проведения функционального моделирования, описание синтезируется в список цепей на вентильном уровне (gate-level) в структурной области (structural). После осуществленимя синтеза можно выполнеть структурное (временное и функциональное) моделирование устройства. В результате мы получаем список цепей (как правило, в формате EDIF) для временной верификации проекта. Рассмотрим применение языков описания аппаратуры высокого уровня (VHDL и Verilog HDL) для описания различных цифровых устройств. Данные описания, если не указывается особо, не ориентированы на какую-либо конкретную систему проектирования или семейство ПЛИС и могут быть воплощены в различных базисах. Такой тип описаний получил название независимого от технологии стиля описания устройств (Technology Independent Coding Styles). Начнем рассмотрение способов описания независимых от технологии устройств с последовательностных устройств (Sequential Devices). В англоязычной литературе приняты термины защелка (latch) для устройств, тактируемых уровнем, и триггер (flip-flop) для устройств, тактируемым фронтом тактового импульса. 6.2. Триггеры и регистры (Flip-Flops, Registers) Для описания триггерных схем в VHDL используются операторы wait и if вместе с процессом, использующим аттрибуты переднего или заднего фронтов синхроимпульса (см. Главу 4). Ниже приведены примеры создания описаний срабатывания по фронту: (clk'event and clk='1') –аттрибут срабатывания по переднему фронту (clk'event and clk='0') -- аттрибут срабатывания по заднему фронту rising_edge(clock) –вызов функции по переднему фронту falling_edge(clock) -- вызов функции по заднему фронту В этих примерах иллюстрируется применение аттрибута переднего фронта (rising edge 'event attribute). Использование аттрибутов следует рекомендовать в тех случаях, когда система проектирования не поддерживает вызов функции по событию. Однако , использование функций позволяет избежать коллизий, связанных с переходом из неопределенного состояния, поскольку функция определяет только переходы уровней (из 0 в 1 или из 1 в 0), не не переход из неопределенного состояния в 1 или 0. Это становиться достаточно важным в случае использования многозначных типов данных, например std_logic, который имеет 9 возможных значений(U, X, 0, 1, Z, W, L, H, -). Далее рассмотрим основные типы триггеров. Триггеры, тактируемые передним фронтом (Rising Edge Flip-Flop) Ниже приводиться пример описания D триггера без цепей асинхронного сброса (reset) или предустановки (preset). На Рис. 1 приведено схемное обозначение рассматриваемого триггера. Рис.6.1 Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff is port (data, clk : in std_logic; q :out std_logic); end dff; architecture behav of dff is begin process (clk) begin if (clk'event and clk = '1') then q <= data; end if; end process; end behav; Описание на Verilog module dff (data, clk, q); input data, clk; output q; reg q; always @(posedge clk) q = data; endmodule Триггеры, тактируемые передним фронтом с асинхронным сбросом (Rising Edge Flip-Flop with Asynchronous Reset) Обозначение устройства приведено на Рис.6.2 Рис.6.2. Пример описания на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff_async_rst is port (data, clk, reset : in std_logic; q :out std_logic); end dff_async_rst; architecture behav of dff_async_rst is begin process (clk, reset) begin if (reset = '0') then q <= '0'; elsif (clk'event and clk = '1') then q <= data; end if; end process; end behav; Пример описания на Verilog module dff_async_rst (data, clk, reset, q); input data, clk, reset; output q; reg q; always @(posedge clk or negedge reset) if (~reset) q = 1'b0; else q = data; endmodule Триггеры, тактируемые передним фронтом с асинхронной предустановкой (Rising Edge Filp-Flop with Asynchronous Preset) Обозначение устройства на схеме приведено на Рис.6.3 Рис.6.3 Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff_async_pre is port (data, clk, preset : in std_logic; q :out std_logic); end dff_async_pre; architecture behav of dff_async_pre is begin process (clk, preset) begin if (preset = '0') then q <= '1'; elsif (clk'event and clk = '1') then q <= data; end if; end process; end behav; Описание на Verilog module dff_async_pre (data, clk, preset, q); input data, clk, preset; output q; reg q; always @(posedge clk or negedge preset) if (~preset) q = 1'b1; else q = data; endmodule Триггеры, тактируемые передним фронтом с асинхронным сбросом и предустановкой (Rising Edge Filp-Flop with Asynchronous Reset and Preset) Обозначение на схеме приведено на Рис.6.4 Рис.6.4 Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff_async is port (data, clk, reset, preset : in std_logic; q :out std_logic); end dff_async; architecture behav of dff_async is begin process (clk, reset, preset) begin if (reset = '0') then q <= '0'; elsif (preset = '1') then q <= '1'; elsif (clk'event and clk = '1') then q <= data; end if; end process; end behav; Описание на Verilog module dff_async (reset, preset, data, q, clk); input clk; input reset, preset, data; output q; reg q; always @(posedge clk or negedge reset or posedge preset) if (~reset) q = 1'b0; else if (preset) q = 1'b1; else q = data; endmodule Триггеры, тактируемые передним фронтом с синхронным сбросом (Rising Edge Filp-Flop with Synchronous Reset) Обозначение на схеме приведено на Рис.6.5 Рис.6.5. Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff_sync_rst is port (data, clk, reset : in std_logic; q :out std_logic); end dff_sync_rst; architecture behav of dff_sync_rst is begin process (clk) begin if (clk'event and clk = '1') then if (reset = '0') then q <= '0'; else q <= data; end if; end if; end process; end behav; Описание на Verilog module dff_sync_rst (data, clk, reset, q); input data, clk, reset; output q; reg q; always @(posedge clk) if (~reset) q = 1'b0; else q = data; endmodule Триггеры, тактируемые передним фронтом с синхронной предустановкой (Rising Edge Filp-Flop with Synchronous Preset) Обозначение на схеме приведено на Рис.6.6 Рис.6.6 Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff_sync_pre is port (data, clk, preset : in std_logic; q :out std_logic); end dff_sync_pre; architecture behav of dff_sync_pre is begin process (clk) begin if (clk'event and clk = '1') then if (preset = '0') then q <= '1'; else q <= data; end if; end if; end process; end behav; Описание на Verilog module dff_sync_pre (data, clk, preset, q); input data, clk, preset; output q; reg q; always @(posedge clk) if (~preset) q = 1'b1; else q = data; endmodule Триггеры, тактируемые передним фронтом с асинхронным сбросом и разрешением тактового сигнала (Rising Edge Filp-Flop with Asynchronous Reset and Clock Enable) Пример схемного обозначения приведен на Рис.6.7 Рис.6. 7. Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity dff_ck_en is port (data, clk, reset, en : in std_logic; q :out std_logic); end dff_ck_en; architecture behav of dff_ck_en is begin process (clk, reset) begin if (reset = '0') then q <= '0'; elsif (clk'event and clk = '1') then if (en = '1') then q <= data; end if; end if; end process; end behav; Описание на Verilog module dff_ck_en (data, clk, reset, en, q); input data, clk, reset, en; output q; reg q; always @(posedge clk or negedge reset) if (~reset) q = 1'b0; else if (en) q = data; endmodule Далее рассмотрим защелки на основе D-триггеров (D-Latches) Защелка с разрешением выхода (D-Latch with Data and Enable) Обозначение на схеме приведено на Рис.6.8 Рис.6.8 Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity d_latch is port(enable, data: in std_logic; y :out std_logic); end d_latch; architecture behave of d_latch is begin process (enable, data) begin if (enable = '1') then y <= data; end if; end process; end behave; Описание на Verilog module d_latch (enable, data, y); input enable, data; output y; reg y; always @(enable or data) if (enable) y = data; endmodule Защелка с входом данных с разрешением (D-Latch with Gated Asynchronous Data) Пример обозначения приведен на Рис.6.9 Рис.6.9. Пример на VHDL library IEEE; use IEEE.std_logic_1164.all; entity d_latch_e is port (enable, gate, data : in std_logic; q :out std_logic); end d_latch_e; architecture behave of d_latch_e is begin process (enable, gate, data) begin if (enable = '1') then q <= data and gate; end if; end process; end behave; Пример на Verilog module d_latch_e(enable, gate, data, q); input enable, gate, data; output q; reg q; always @ (enable or data or gate) if (enable) q = (data & gate); endmodule Защелка с входом разрешения (D-Latch with Gated Enable) (Рис.6.10) Рис.6.10 Пример на VHDL library IEEE; use IEEE.std_logic_1164.all; entity d_latch_en is port (enable, gate, d: in std_logic; q :out std_logic); end d_latch_en; architecture behave of d_latch_en is begin process (enable, gate, d) begin if ((enable and gate) = '1') then q <=d; end if; end process; end behave; Пример на Verilog module d_latch_en(enable, gate, d, q); input enable, gate, d; output q; reg q; always @ (enable or d or gate) if (enable & gate) q =d; endmodule Защелка с асинхронным сбросом (D-Latch with Asynchronous Reset) (Рис.6.11) Рис.6.11. Пример описания на VHDL library IEEE; use IEEE.std_logic_1164.all; entity d_latch_rst is port (enable, data, reset: in std_logic; q :out std_logic); end d_latch_rst; architecture behav of d_latch_rst is begin process (enable, data, reset) begin if (reset = '0') then q <= '0'; elsif (enable = '1') then q <= data; end if; end process; end behav; Пример описания на Verilog module d_latch_rst (reset, enable, data, q); input reset, enable, data; output q; reg q; always @ (reset or enable or data) if (~reset) q = 1'b0; else if (enable) q = data; endmodule 6.3. Построение устройств потоковой обработки данных (Datapath logic) Как правило, устройства потоковой обработки данных представляют собой структурированные, повторяющиеся функции. Рассмотрение таких устройств начнем с приоритетного шифратора. Приоритетный шифратор (Рис.6.12) строиться с использованием оператора If-Then-Else Рис.6.12 Пример описания шифратора на VHDL library IEEE; use IEEE.std_logic_1164.all; entity my_if is port (c, d, e, f: in std_logic; s :in std_logic_vector(1 downto 0); pout : out std_logic); end my_if; architecture my_arc of my_if is begin myif_pro: process (s, c, d, e, f) begin if s = “00” then pout <= c; elsif s = “01” then pout <= d; elsif s = “10” then pout <= e; else pout <= f; end if; end process myif_pro; end my_arc; Пример описания на Verilog module IF_MUX (c, d, e, f, s, pout); input c, d, e, f; input [1:0]s; output pout; reg pout; always @(c or d or e or f or s) begin if (s == 2'b00) pout = c; else if (s ==2'b01) pout = d; else if (s ==2'b10) pout = e; else pout = f; end endmodule Другим часто используемым устройством является мультиплексор (Multiplexors). Как правило, для построения мультиплексора удобно использовать оператор Case. Оператор case обеспечивает параллельную обработку. Оператор выбора case (case statement) используется для выбора одного варианта из нескольких в зависимости от условий. Средства синтеза с VHDL позволяют автоматически выполнить параллельную обработку без приоритета, в то время, как средства синтеза с Verilog поддерживают приоритет в выполнении оператора case и в ряде случаев необходимо ввести специфические инструкции в код и для того, чтобы оператор выбора не имел приоритета. Рис.6.13 Мультиплексор 4 в 1 Ниже приводятся примеры описания мультиплексора 4 в 1 Пример описания на VHDL --4:1 Multiplexor library IEEE; use IEEE.std_logic_1164.all; entity mux is port (c, d, e, f : in std_logic; s :in std_logic_vector(1 downto 0); muxout : out std_logic); end mux; architecture my_mux of mux is begin mux1: process (s, c, d, e, f) begin case s is when “00” => muxout <= c; when “01” => muxout <= d; when “10” => muxout <= e; when others => muxout <= f; end case; end process mux1; end my_mux; Пример описания на Verilog //4:1 Multiplexor module MUX (C, D, E, F, S, MUX_OUT); input C, D, E, F; input [1:0] S; output MUX_OUT; reg MUX_OUT; always @(C or D or E or F or S) begin case (S) 2'b00 : MUX_OUT = C; 2'b01 : MUX_OUT = D; 2'b10 : MUX_OUT = E; default : MUX_OUT = F; endcase end endmodule Для более сложного случая рассмотрим пример мультиплексора 12 в 1 Пример описания на VHDL -- 12:1 mux library ieee; use ieee.std_logic_1164.all; -- Entity declaration: entity mux12_1 is port ( mux_sel: in std_logic_vector (3 downto 0);-- mux select A: in std_logic; B: in std_logic; C: in std_logic; D: in std_logic; E: in std_logic; F: in std_logic; G: in std_logic; H: in std_logic; I: in std_logic; J: in std_logic; K: in std_logic; M: in std_logic; mux_out: out std_logic -- mux output ); end mux12_1; -- Architectural body: architecture synth of mux12_1 is begin proc1: process (mux_sel, A, B, C, D, E, F, G, H, I, J, K, M) begin case mux_sel is when "0000" => mux_out<= A; when "0001" => mux_out <= B; when "0010" => mux_out <= C; when "0011” => mux_out <= D; when "0100" => mux_out <= E; when "0101" => mux_out <= F; when "0110" when "0111" when "1000" when "1001" when "1010" when "1011" when others end case; end process end synth; => => => => => => => mux_out <= G; mux_out <= H; mux_out <= I; mux_out <= J; mux_out <= K; mux_out <= M; mux_out<= '0'; proc1; Описание мультиплексора 12 в 1на Verilog // 12:1 mux module mux12_1(mux_out, mux_sel,M,L,K,J,H,G,F,E,D,C,B,A ); output mux_out; input [3:0] mux_sel; input M; input L; input K; input J; input H; input G; input F; input E; input D; input C; input B; input A; reg mux_out; // create a 12:1 mux using a case statement always @ ({mux_sel[3:0]} or M or L or K or J or H or G or F or E or D or C or B or A) begin: mux_blk case ({mux_sel[3:0]}) // synthesis full_case parallel_case 4'b0000 : mux_out = A; 4'b0001 : mux_out = B; 4'b0010 : mux_out = C; 4'b0011 : mux_out = D; 4'b0100 : mux_out = E; 4'b0101 : mux_out = F; 4'b0110 : mux_out = G; 4'b0111 : mux_out = H; 4'b1000 : mux_out = J; 4'b1001 : mux_out = K; 4'b1010 : mux_out = L; 4'b1011 : mux_out = M; 4'b1100 : mux_out = 1'b0; 4'b1101 : mux_out = 1'b0; 4'b1110 : mux_out = 1'b0; 4'b1111 : mux_out = 1'b0; endcase end endmodule Кроме обычного опреатора выбора в языке описания аппараатуры Verilog используется оператор выбора casex Ниже приводится описание на Verilog мультиплексора 4 в 1 //8 bit 4:1 multiplexor with don't care X, 3:1 equivalent mux module mux4 (a, b, c, sel, q); input [7:0] a, b, c; input [1:0] sel; output [7:0] q; reg [7:0] q; always @ (sel or a or b or c) casex (sel) 2'b00:q =a; 2'b01:q =b; 2'b1x:q =c; default:q =c; endcase endmodule Дешифратор, пожалуй, является самым распространенным комбинационным устройством. Ниже приводиться пример дешифратора 3 в 8. Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; entity decode is port ( Ain : in std_logic_vector (2 downto 0); En: in std_logic; Yout : out std_logic_vector (7 downto 0)); end decode; architecture decode_arch of decode is begin process (Ain) begin if (En='0') then Yout <= (others => '0'); else case Ain is when "000" => Yout <= "00000001"; when "001" => Yout <= "00000010"; when "010" => Yout <= "00000100"; when "011" => Yout <= "00001000"; when "100" => Yout <= "00010000"; when "101" => Yout <= "00100000"; when "110" => Yout <= "01000000"; when "111" => Yout <= "10000000"; when others => Yout <= "00000000"; end case; end if; end process; end decode_arch; Описание дешифратора на Verilog имеет вид module decode (Ain, En, Yout); input En; input [2:0] Ain; output [7:0] Yout; reg [7:0] Yout; always @ (En or Ain) begin if (!En) Yout = 8'b0; else case (Ain) 3'b000 : Yout = 8'b00000001; 3'b001 : Yout = 8'b00000010; 3'b010 : Yout = 8'b00000100; 3'b011 : Yout = 8'b00001000; 3'b100 : Yout = 8'b00010000; 3'b101 : Yout = 8'b00100000; 3'b110 : Yout = 8'b01000000; 3'b111 : Yout = 8'b10000000; default : Yout = 8'b00000000; endcase end endmodule 6.4. Счетчики Счетчики являются достаточно широко распространенными устройствами, их классификация и принципы построения изложены в литературе [34-38]. Следует помнить, что большинство программ синтеза не позволяют получить приемлимых результатов по быстродействию при разрядности счетчика более 8 бит, в этом случае часто применяются специфические приемы синтеза, зависящие от технологии, по которой выполнена ПЛИС. Рассмотрим пример построения 8 разрядного счетчика, считающего в прямом направлении и имеющего цепи разрешения счета и асинхронного сброса. Пример описания на VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use IEEE.std_logic_arith.all; entity counter8 is port (clk, en, rst : in std_logic; count : out std_logic_vector (7 downto 0)); end counter8; architecture behav of counter8 is signal cnt: std_logic_vector (7 downto 0); begin process (clk, en, cnt, rst) begin if (rst = '0') then cnt <= (others => '0'); elsif (clk'event and clk = '1') then if (en = '1') then cnt <= cnt + '1'; end if; end process; count <= cnt; end behav; Пример на Verilog module count_en (en, clock, reset, out); parameter Width = 8; input clock, reset, en; output [Width-1:0] out; reg [Width-1:0] out; always @(posedge clock or negedge reset) if(!reset) out = 8'b0; else if(en) out =out +1; endmodule Другой пример иллюстрирует построение 8 разрядного счетчика с загрузкой и асинхронным сбросом (8-bit Up Counter with Load and Asynchronous Reset) Пример на VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use IEEE.std_logic_arith.all; entity counter is port (clk, reset, load: in std_logic; data: in std_logic_vector (7 downto 0); count: out std_logic_vector (7 downto 0)); end counter; architecture behave of counter is signal count_i : std_logic_vector (7 downto 0); begin process (clk, reset) begin if (reset = '0') then count_i <= (others => '0'); elsif (clk'event and clk = '1') then if load = '1' then count_i <= data; else count_i <= count_i + '1'; end if; end if; end process; count <= count_i; end behave; Описание на Verilog module count_load (out, data, load, clk, reset); parameter Width = 8; input load, clk, reset; input [Width-1:0] data; output [Width-1:0] out; reg [Width-1:0] out; always @(posedge clk or negedge reset) if(!reset) out = 8'b0; else if(load) out = data; else out =out +1; endmodule Пример построения счетчика с предварительной загрузкой, входами разрешения и остановом счета (8-bit Up Counter with Load, Count Enable, Terminal Count and Asynchronous Reset) приведен ниже. Описание на Verilog module count_load (out, cout, data, load, clk, en, reset); parameter Width = 8; input load, clk, en, reset; input [Width-1:0] data; output cout; // carry out output [Width-1:0] out; reg [Width-1:0] out; always @(posedge clk or negedge reset) if(!reset) out = 8'b0; else if(load) out = data; else if(en) out =out +1; // cout=1 when all out bits equal 1 assign cout = &out; endmodule Следующий пример – счетчик с произвольным модулем счета и всеми остальными функциями – сброс, загрузка и т.п. (N-bit Up Counter with Load, Count Enable, and Asynchronous Reset) Пример на VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use IEEE.std_logic_arith.all; entity counter is generic (width : integer := n); port (data : in std_logic_vector (width-1 downto 0); load, en, clk, rst : in std_logic; q :out std_logic_vector (width-1 downto 0)); end counter; architecture behave of counter is signal count : std_logic_vector (width-1 downto 0); begin process(clk, rst) begin if rst = '1' then count <= (others => '0'); elsif (clk'event and clk = '1') then if load = '1' then count <= data; elsif en = '1' then count <= count + 1; end if; end if; end process; q <= count; end behave; 6.5. Арифметические устройства Проектирование устройств обработки информации немыслимо без реализациии арифметических опреаций – сложения, умножения, вычитания и деления. Ниже приводяться примеры использования арифметических операторов при проектировании на языках описания аппаратуры высокого уровня. Пример выполнения арифметических операций приведен ниже. Описание на VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity arithmetic is port (A, B: in std_logic_vector(3 downto 0); Q1: out std_logic_vector(4 downto 0); Q2, Q3: out std_logic_vector(3 downto 0); Q4: out std_logic_vector(7 downto 0)); end arithmetic; architecture behav of arithmetic is begin process (A, B) begin Q1 <= ('0' & A) + ('0' & B); --addition Q2<=A -B;--subtraction Q3<=A /B;--division Q4<=A *B;--multiplication end process; end behav; Конечно, такое описание не всегда (скорее никогда) не приводит к хорошим результатам. Ниже мы еще вернемся к теме построения быстродействующих арифметических устройств. Здесь же заметим, что если требуется выполнить умножени или деление на число, являющееся степенью двойки, то арифметическая операция легко выполняется путем сдвига на необходимое число разрядов вправо или влево. Например выражение: Q <= C/16 + C*4; может быть представлено как Q <= shr (C, “100”) + shl (C, “10”); Или на VHDL Q <= “0000”&C (8downto 4)+C (6downto 0) & ”00”; Функции “shr” и “shl” находятся в пакете IEEE.std_logic_arith.all. Пример арифметических операций в Verilog module arithmetic (A, B, Q1, Q2, Q3, Q4); input [3:0] A, B; output [4:0] Q1; output [3:0] Q2, Q3; output [7:0] Q4; reg [4:0] Q1; reg [3:0] Q2, Q3; reg [7:0] Q4; always @(Aor B) begin Q1 =A +B;//addition Q2 =A -B;//subtraction Q3 =A /2;//division Q4 =A *B;//multiplication end endmodule Реализация регистров сдвига имеет вид Q = {4b'0000 C[8:4]} + {C[6:0] 2b'00}; Опреаторы отношения сравнивают два операнда и выдают значение истинно или ложно (true or false). Приведенные ниже примеры показывают применение операторов отношения. Пример использования операторов отношения в VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; entity relational is port (A,B :in std_logic_vector(3 downto 0); Q1, Q2, Q3, Q4 : out std_logic); end relational; architecture behav of relational is begin process (A, B) begin --Q1<=A >B;--greater than --Q2<=A <B;--less than -- Q3 <= A >= B; -- greater than equal to if (A <= B) then –- less than equal to Q4 <= '1'; else Q4 <= '0'; end if; end process; end behav; Оераторы отношения в Verilog module relational (A, B, Q1, Q2, Q3, Q4); input [3:0] A, B; output Q1, Q2, Q3, Q4; reg Q1, Q2, Q3, Q4; always @(Aor B) begin //Q1 =A >B;//greater than //Q2 =A <B;//less than //Q3 =A >=B;//greater than equal to if (A <= B) //less than equal to Q4 =1; else Q4 =0; end endmodule Оператор эквивалентности (Equality Operator) используется для сравнения операндов. Пример реализации оператора эквивалентности на VHDL library IEEE; use IEEE.std_logic_1164.all; entity equality is port ( A: in STD_LOGIC_VECTOR (3 downto 0); B: in STD_LOGIC_VECTOR (3 downto 0); Q1: out STD_LOGIC; Q2: out STD_LOGIC ); end equality; architecture equality_arch of equality is begin process (A, B) begin Q1<=A =B;--equality if (A /= B) then -- inequality Q2 <= '1'; else Q2 <= '0'; end if; end process; end equality_arch; Другой вариант описания на VHDL имеет вид library IEEE; use IEEE.std_logic_1164.all; entity equality is port ( A: in STD_LOGIC_VECTOR (3 downto 0); B: in STD_LOGIC_VECTOR (3 downto 0); Q1: out STD_LOGIC; Q2: out STD_LOGIC ); end equality; architecture equality_arch of equality is begin Q1 <= '1' when A =Belse '0'; -- equality Q2 <= '1' when A /=Belse '0'; -- inequality end equality_arch; Описание на Verilog module equality (A, B, Q1, Q2); input [3:0] A; input [3:0] B; output Q1; output Q2; reg Q1, Q2; always @(A or B) begin Q1 =A ==B;//equality if (A != B) //inequality Q2 =1; else Q2 =0; end endmodule Реализация сдвигателей (Shift operators) может быть полезна в различных схемах умножения – деления, а также нормализации данных Пример сдвигателя на VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity shift is port (data : in std_logic_vector(3 downto 0); q1, q2 : out std_logic_vector(3 downto 0)); end shift; architecture rtl of shift is begin process (data) begin q1 <= shl (data, "10"); -- logical shift left q2 <= shr (data, "10"); --logical shift right end process; end rtl; Другой вариант сдвигателя library IEEE; use IEEE.std_logic_1164.all; entity shift is port (data : in std_logic_vector(3 downto 0); q1, q2 : out std_logic_vector(3 downto 0)); end shift; architecture rtl of shift is begin process (data) begin q1 <= data(1 downto 0) & “10”; -- logical shift left q2 <= “10” & data(3 downto 2); --logical shift right end process; end rtl; Описание на Verilog module shift (data, q1, q2); input [3:0] data; output [3:0] q1, q2; parameter B =2; reg [3:0] q1, q2; always @ (data) begin q1 = data << B; // logical shift left q2 = data >> B; //logical shift right end endmodule 6.6.Конечные автоматы (Finite State Machine) Конечные автоматы (Finite State Machine) и средства их проектирования быле рассмотрены выше (см. Главу.2). В ряде случаев автоматная модель (описание) устройства позволяют получить быструю и эффективную реализацию последовательностного устройства. Как известно, обычно рассматривают два типа автоматов – автомат Мили (Mealy) и Мура (Moore). Выход автомата Мура является функцией только текущего состояния, в то время, как выход автомата Мили функция как текущего состояния, так и начального внешнего воздействия. Обычно конечный автомат состоит из трех основных частей: Регистр текущего состояния (Sequential Current State Register). Этот регистр представляет собой набор тактируемых D-триггеров синхронизируемых одним синхросигналом и используется для хранения кода текущего состояния автомата. Очевидно, что для автомата с n состояниями требуется log 2 (n) триггеров. Логика переходов (Combinational Next State Logic). Как известно, конечный автомат может находиться в каждый конкретный момент времени только в одном состоянии. Каждый тактовый импульс вызывает переход автомата из одного состояния в другое. Правила перехода и определяются комбинационной схемой, называемой логикой переходов. Следующее состояние определяется как функция текущего состояния и входного воздействия. Логика формирования выхода (Combinational Output Logic). Выход цифрового автомата обычно определяется как функция текущего состояния и исходной установки (в случае автомата Мили). Формирование выходного сигнала автомата определяется с помощью логики формирования выхода. На Рис. 6.14 и 6.15 приведены структуры автоматов Мура и Мили соответственно. Рис.6.14. Автомат Мура Рис.6.15. Автомат Мили Для обеспечения стабильной и безотказной работы используется сброс автомата в начальное состояние. Таким образом всегда обеспечивается инициализация автомата в заранее предорпеделенном сотоянии при первом тактовом импульсе. В случае, если сброс не предусмотрен, невозможно предсказать с какого начального состояния начнет функционирование, что может привести к сбоям в работе всей системы. Особенно эта ситуация актуальна при включении питания системы. Поэтому мы настоятельно рекоммендуем использовать схемы сброса и начальной установки при проектировании устройств на ПЛИС. Обычно применяют асинхронные схемы сброса, из-за того, что не требуется использовать дешифратор неиспользуемых (избыточных) состояний, что упрощает логику переходов. С другой стороны, из-за того, что ПЛИС, выполненные по архитектуре FPGA имеют достаточное число регистров (триггеров), использование автоматных моделей позволяет получить достаточно быстродействующую и, в то же время наглядную реализацию при приемлимых затратах ресурсов. Ниже рассмотрим пример проектирования автомата Мили. Диаграмма переходов автомата приведена на Рис.6.16. Рис.6.16. Диаграмма переходов автомата Мили. Пример автомата Мили на VHDL -- Автомат Мили с 5 состояниями library ieee; use ieee.std_logic_1164.all; entity mealy is port (clock, reset: in std_logic; data_out: out std_logic; data_in: in std_logic_vector (1 downto 0)); end mealy; architecture behave of mealy is type state_values is (st0, st1, st2, st3, st4); signal pres_state, next_state: state_values; begin -- FSM register statereg: process (clock, reset) begin if (reset = '0') then pres_state <= st0; elsif (clock'event and clock ='1') then pres_state <= next_state; end if; end process statereg; -- FSM combinational block fsm: process (pres_state, data_in) begin case pres_state is when st0 => case data_in is when "00" => next_state <= st0; when "01" => next_state <= st4; when "10" => next_state <= st1; when "11" => next_state <= st2; when others => next_state <= (others <= ‘x’); end case; when st1 => case data_in is when "00" => next_state <= st0; when "10" => next_state <= st2; when others => next_state <= st1; end case; when st2 => case data_in is when "00" => next_state <= st1; when "01" => next_state <= st1; when "10" => next_state <= st3; when "11" => next_state <= st3; when others => next_state <= (others <= ‘x’); end case; when st3 => case data_in is when "01" => next_state <= st4; when "11" => next_state <= st4; when others => next_state <= st3; end case; when st4 => case data_in is when "11" => next_state <= st4; when others => next_state <= st0; end case; when others => next_state <= st0; end case; end process fsm; -- Mealy output definition using pres_state w/ data_in outputs: process (pres_state, data_in) begin case pres_state is when st0 => case data_in is when "00" => data_out <= '0'; when others => data_out <= '1'; end case; when st1 => data_out <= '0'; when st2 => case data_in is when "00" => data_out <= '0'; when "01" => data_out <= '0'; when others => data_out <= '1'; end case; when st3 => data_out <= '1'; when st4 => case data_in is when "10" => data_out <= '1'; when "11" => data_out <= '1'; when others => data_out <= '0'; end case; when others => data_out <= '0'; end case; end process outputs; end behave; Описание автомата на Verilog имеет вид // Example of a 5-state Mealy FSM module mealy (data_in, data_out, reset, clock); output data_out; input [1:0] data_in; input reset, clock; reg data_out; reg [2:0] pres_state, next_state; parameter st0=3'd0, st1=3'd1, st2=3'd2, st3=3'd3, st4=3'd4; // FSM register always @(posedge clock or negedge reset) begin: statereg if(!reset)// asynchronous reset pres_state = st0; else pres_state = next_state; end // statereg // FSM combinational block always @(pres_state or data_in) begin: fsm case (pres_state) st0: case(data_in) 2'b00: next_state=st0; 2'b01: next_state=st4; 2'b10: next_state=st1; 2'b11: next_state=st2; endcase st1: case(data_in) 2'b00: next_state=st0; 2'b10: next_state=st2; default: next_state=st1; endcase st2: case(data_in) 2'b0x: next_state=st1; 2'b1x: next_state=st3; endcase st3: case(data_in) 2'bx1: next_state=st4; default: next_state=st3; endcase st4: case(data_in) 2'b11: next_state=st4; default: next_state=st0; endcase default: next_state=st0; endcase end // fsm // Mealy output definition using pres_state w/ data_in always @(data_in or pres_state) begin: outputs case(pres_state) st0: case(data_in) 2'b00: data_out=1'b0; default: data_out=1'b1; endcase st1: data_out=1'b0; st2: case(data_in) 2'b0x: data_out=1'b0; default: data_out=1'b1; endcase st3: data_out=1'b1; st4: case(data_in) 2'b1x: data_out=1'b1; default: data_out=1'b0; endcase default: data_out=1'b0; endcase end // outputs endmodule Пример автомата Мура (Moore Machine) с тем же графом переходов имеет вид. Описание на VHDL -- Example of a 5-state Moore FSM library ieee; use ieee.std_logic_1164.all; entity moore is port (clock, reset: in std_logic; data_out: out std_logic; data_in: in std_logic_vector (1 downto 0)); end moore; architecture behave of moore is type state_values is (st0, st1, st2, st3, st4); signal pres_state, next_state: state_values; begin -- FSM register statereg: process (clock, reset) begin if (reset = '0') then pres_state <= st0; elsif (clock ='1' and clock'event) then pres_state <= next_state; end if; end process statereg; -- FSM combinational block fsm: process (pres_state, data_in) begin case pres_state is when st0 => case data_in is when "00" => next_state <= st0; when "01" => next_state <= st4; when "10" => next_state <= st1; when "11" => next_state <= st2; when others => next_state <= (others <= ‘x’); end case; when st1 => case data_in is when "00" => next_state <= st0; when "10" => next_state <= st2; when others => next_state <= st1; end case; when st2 => case data_in is when "00" => next_state <= st1; when "01" => next_state <= st1; when "10" => next_state <= st3; when "11" => next_state <= st3; when others => next_state <= (others <= ‘x’); end case; when st3 => case data_in is when "01" => next_state <= st4; when "11" => next_state <= st4; when others => next_state <= st3; end case; when st4 => case data_in is when "11" => next_state <= st4; when others => next_state <= st0; end case; when others => next_state <= st0; end case; end process fsm; -- Moore output definition using pres_state only outputs: process (pres_state) begin case pres_state is when st0 => data_out <= '1'; when st1 => data_out <= '0'; when st2 => data_out <= '1'; when st3 => data_out <= '0'; when st4 => data_out <= '1'; when others => data_out <= '0'; end case; end process outputs; end behave; Описание автомата Мура на Verilog // Example of a 5-state Moore FSM module moore (data_in, data_out, reset, clock); output data_out; input [1:0] data_in; input reset, clock; reg data_out; reg [2:0] pres_state, next_state; parameter st0=3'd0, st1=3'd1, st2=3'd2, st3=3'd3, st4=3'd4; //FSM register always @(posedge clock or negedge reset) begin: statereg if(!reset) pres_state = st0; else pres_state = next_state; end // statereg // FSM combinational block always @(pres_state or data_in) begin: fsm case (pres_state) st0: case(data_in) 2'b00: next_state=st0; 2'b01: next_state=st4; 2'b10: next_state=st1; 2'b11: next_state=st2; endcase st1: case(data_in) 2'b00: next_state=st0; 2'b10: next_state=st2; default: next_state=st1; endcase st2: case(data_in) 2'b0x: next_state=st1; 2'b1x: next_state=st3; endcase st3: case(data_in) 2'bx1: next_state=st4; default: next_state=st3; endcase st4: case(data_in) 2'b11: next_state=st4; default: next_state=st0; endcase default: next_state=st0; endcase end // fsm // Moore output definition using pres_state only always @(pres_state) begin: outputs case(pres_state) st0: data_out=1'b1; st1: data_out=1'b0; st2: data_out=1'b1; st3: data_out=1'b0; st4: data_out=1'b1; default: data_out=1'b0; endcase end // outputs endmodule // Moore 6.7. Элементы ввода – вывода Без элементов ввода вывода невозможна организация полноценного обмена данных, организация шин и т.п. Рассмотрим особенности описания на языках описания аппаратуры элементов ввода – вывода (Input-Output Buffers). Элемент с тремя состояниями (Tri-State Buffer) позволяет организовать подключение нескольких устройств к общей шине. Обозначение такого элемента на схеме приведено на Рис.6.17 Рис.6.17. Элемент с тремя состояниями. Пример описания на VHDL library IEEE; use IEEE.std_logic_1164.all; entity tristate is port (e,a :in std_logic; y :out std_logic); end tristate; architecture tri of tristate is begin process (e, a) begin if e = '1' then y <=a; else y <= 'Z'; end if; end process; end tri; Другой вариант описания элемента с тремя состояниями на VHDL library IEEE; use IEEE.std_logic_1164.all; entity tristate is port (e,a :in std_logic; y :out std_logic); end tristate; architecture tri of tristate is begin Y <=awhen (e = '1') else 'Z'; end tri; На Verilog элемент с тремя состояниями описывается следующим образом module TRISTATE (e, a, y); input a, e; output y; reg y; always @(e or a) begin if (e) y =a; else y = 1'bz; end endmodule OR module TRISTATE (e, a, y); input a, e; output y; assign y =e ?a :1'bZ; endmodule Приведенные выше примеры показывают логику функционирования элемента с тремя сростояниями. Примеры создания экземрляра компонента приведены ниже. Экземпляр компонента на VHDL library IEEE; use IEEE.std_logic_1164.all; entity tristate is port (e,a :in std_logic; y :out std_logic); end tristate; architecture tri of tristate is component TRIBUFF port (D, E: in std_logic; PAD: out std_logic); end component; begin U1: TRIBUFF port map (D => a, E =>e, PAD => y); end tri; Экземпляр компонента на Verilog module TRISTATE (e, a, y); input a, e; output y; TRIBUFF U1 (.D(a),.E(e),.PAD(y)); endmodule Двунаправленные элементы ввода-вывода (Bi-Directional Buffer) Двунаправленный элемент ввода-вывода используется либо как элемент ввода, либо как выходной буфер с возможностью перехода в третье состояние. Ниже приводятся примеры создания описания двунаправленного буфера (Рис.618). Рис.6.18. Пример логики двунаправленного буфера на VHDL library IEEE; use IEEE.std_logic_1164.all; entity bidir is port (y : inout std_logic; e, a: in std_logic; b :out std_logic); end bidir; architecture bi of bidir is begin process (e, a) begin case e is when '1'=>y <=a; when '0' => y <= 'Z'; when others => y <= 'X'; end case; end process; b <=y; end bi; Описание двунаправленного буфера на Verilog module bidir (e, y, a, b); input a, e; inout y; output b; reg y_int; wire y, b; always @(a or e) begin if (e == 1'b1) y_int <= a; else y_int <= 1'bz; end assign y = y_int; assign b =y; endmodule Примеры создания экземпляров компонента на VHDL library IEEE; use IEEE.std_logic_1164.all; entity bidir is port (y : inout std_logic; e, a: in std_logic; b :out std_logic); end bidir; architecture bi of bidir is component BIBUF port (D, E: in std_logic; Y :out std_logic; PAD: inout std_logic); end component; begin U1: BIBUF port map (D => a, E =>e, Y =>b, PAD => y); end bi; Описание экземпляра буфера на Verilog module bidir (e, y, a, b); input a, e; inout y; output b; BIBUF U1 (.PAD(y),.D(a),.E(e),.Y(b) ); Endmodule 6.8.Параметризация Как уже отмечалось выше, в последние годы наметилась тенденция к разработке и использованию параметризованных модулей (ядер, макросов, мегафункций) при проектировании устройств на ПЛИС с использованием языков описания аппаратуры. В языках описания аппаратуры существуют специальные конструкции, позволяющие обеспечить полную параметризацию проекта. Это так называемые родовые (параметризованные, настраиваемые) типы данных (Generics) и параметры (Parameters). Мы будем использовать термин настраиваемый тип. Насираиваемые типы данных и параметры используются обычно для определения размерности компонента (например разрядность шин, коэффициентов и т.п.) Конкретное значение параметров определяется при создании конкретного экземпляра компонента. Рассмотрим пример использования параметризации на примере сумматора. Описание параметризируемого сумматора на VHDL library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity adder is generic (WIDTH : integer := 8); port (A, B: in UNSIGNED(WIDTH-1 downto 0); CIN: in std_logic; COUT: out std_logic; Y: out UNSIGNED(WIDTH-1 downto 0)); end adder; architecture rtl of adder is begin process (A,B,CIN) variable TEMP_A,TEMP_B,TEMP_Y:UNSIGNED(A'length downto 0); begin TEMP_A := '0' & A; TEMP_B := '0' & B; TEMP_Y := TEMP_A + TEMP_B + CIN; Y <= TEMP_Y (A'length-1 downto 0); COUT <= TEMP_Y (A'length); end process; end rtl; Параметр “Width” определяет разрядность данных. Пример конкретного экземпляра компонента с 16 разрядными данными приведен ниже: U1: adder generic map(16) port map (A_A, B_A, CIN_A, COUT_A, Y_A); Описание сумматора на Verilog module adder (cout, sum, a, b, cin); parameter Size = 8; output cout; output [Size-1:0] sum; input cin; input [Size-1:0] a, b; assign {cout, sum}=a +b +cin; endmodule Параметр “Size” определяет ширину сумматора. Реализация компонента 16 битного сумматора приведена ниже: adder #(16) adder16(cout_A, sun_A, a_A, b_A, cin_A) 6.9. Спрецифика проектирования устройств с учетом архитектурных особенностей ПЛИС В отличие от специализированных БИС (ASIC), ПЛИС выполненные по FPGA архитектуре имеют модульную матричную структуру. Каждая комбинационная ступень реализации сложной функции вносит свой вклад в суммарную задержку распространения сигнала. В результате приходиться учитывать ограничения, связанные с слишком длинными цепями распространения сигналов. Использование соответствующих приемов описания устройств (coding style) позволяет улучшить временные характеристики спроекированного устройства. Рассмотрим способы снижения числа ступеней вычисления сложной функции (Reducing Logic Levels) для уменьшения длины критических путей распространения сигнала (Critical Paths). Ниже приводятся примеры оптимального проектирования таких устройств. Пример 1. В данном примере сигнал “critical” (Рис.6.19) проходит через три ступени (logic levels). Рис.6.19. Пример написан на языке VHDL. if ((( Crtical='0' and Obi='1' and Sar='1') or CpuG='0') and CpuR='0') then Des <= Adr; elsif (((Crtical='0' and Obi='1' and Sar='1') or CpuG='0') and CpuR='1') then Des <= Bdr; elsif (Sar='0' and.......... В результате сигнал “critical” является запаздывающим. Чтобы уменьшить число ступеней используем конструкцию if-then-else statement. В результате сигнал “critical” проходит только через одну ступень как показано на Рис.6.20 Рис.6.20 if (Critical='0') then if (((Obi='1' and Sar='1') or CpuG='0') and CpuR='0') then Des <= Adr; elsif (((Obi='1' and Sar='1') or CpuG='0' and CpuR='1') then Des <= Bdr; end if; end if; Пример 2. В данном случае сигнал, обозначенный “critical” (Рис.6.21), проходит через две ступени. Рис.6.21. if (clk'event and clk ='1') then if (non_critical and critical) then out1 <= in1 else out1 <= in2 end if; end if; Для уменьшения числа ступений для сигнала “critical”, используем мультиплексирование входных сигналов “in1” и “in2” используя для управления мультиплексором сигнал “non_critical”. Выход мультиплексора затем мультиплексируется с входом “in2” при управлении сигналом “critical”. В итоге сигнал “critical” проходит только через одну ступень задержки (Рис.6.22) Рис.6.22 Реализация на VHDL приведена ниже signal out_temp : std_logic if (non_critical) out_temp <= in1; else out_temp <= in2; if (clk'event and clk ='1') then if (critical) then out1 <= out_temp; else out1 <= in2; end if; end if; end if; 6.10. Совместное использование ресурсов Другим путем повышения эффективности описаний цифровых устройств является совместное использование ресурсов (Resource Sharing). Совместное использование ресурсов позволяет снизить число логических элементов для реализации операторов на языках описания аппаратуры. Ниже приводяться два примера совместного использования ресурсов. Примеры написаны на Пример 1. Реализация четырех сумматоров (счетчиков) if (...(siz == 1)...) count = count + 1; else if (...((siz ==2)...) count = count + 2; else if (...(siz == 3)...) count = count + 3; else if (...(siz == 0)...) count = count + 4; Покажем, как можно избавиться от двух «лишних» счетчиков if (...(siz == 0)...) count = count + 4; else if (...) count = count + siz Пример 2. В данном примере используется неполное совместное использование ресурсов для реализации сумматоров (Рис.6.23). Рис.6.23. if (select) sum<=A +B; else sum<=C +D; Сумматоры занимают значительные ресурсы, для их уменьшения переписываем код с целью ввести два мультиплексора и один сумматор, как показано ниже (Рис.6.24). Рис.6. 24. if (sel) temp1 <= A; temp2 <= B; else temp1 <= C; temp2 <= D; sum <= temp1 + temp2; Следует помнить, что в данном примере сигнал выбора «sel» не является запаздывающим сигналом Еще одним способом организации совместного использования ресурсов является использование операторов цикла Арифметические операторы и мультиплексоры занимают значительные ресурсы. Если имеется оператор внутри цикла, программа синтеза должна оценить все состояния. В следующем примере на языке VHDL описываются четыре сумматора и один мультиплексор. Такая реализация может быть рекомендована только, если сигнал выбора "req" – запаздывающий сигнал (Рис.6.25). Рис.6.25. vsum := sum; for i in 0 to 3 loop if (req(i)='1') then vsum <= vsum + offset(i); end if; end loop; Если сигнал выбора “req” не является критическим, оператор может быть вынесен за пределы цикла, что приведет к тому, что вместо четырех сумматоров будет использован один (Рис.6.26) Рис.6.26 Пример описания приведен ниже vsum := sum; for i in 0 to 3 loop if (req(i)='1') then offset_1 <= offset(i); end if; end loop; vsum <= vsum + offset_1; Использование кодирования для сочетаемости (Coding for Combinability) Использование кодирования для сочетаемости (Coding for Combinability) также позволяет выкроить ресурсы ПЛИС. Для ПЛИС некоторых архитектур возможно выполнение всей логики работы, как комбинационной, так и последовательностной в пределах одного логического элемента (ячейки), что значительно снижает задержки по критическим путям и экономит ресурсы. Однако следует помнить, что такое объединение возможно только в том случае, если комбинационная схема управляет только одним триггером. В приведенном примере описания на VHDL элемент AND формирует управляющий сигнал для двух триггеров (Рис.6.27), что препятствует введению элемента AND в последовательностный модуль Рис.6.27 one :process (clk, a, b, c, en) begin if (clk'event and clk ='1') then if (en = '1') then q2 <= a and b and c; end if; q1 <= a and b and c; end if; end process one; Для того, чтобы объединить схему внутри одного элемента и убрать внешние цепи удобно использовать дублирование логического элемента AND, уменьшая тем самым критические пути распространения сигнала (Рис.6.28). Рис.6.28. part_one: process (clk, a, b, c, en) begin if (clk'event and clk ='1') then if (en = '1') then q2 <= a and b and c; end if; end if; end process part_one; part_two: process (clk, a, b, c) begin if (clk'event and clk ='1') then q1 <= a and b and c; end if; end process part_two; 6.12. Дублирование регистра. Задержка в цепи распространения сигнала возрастает с увеличением числа входов, нагруженных на эту цепь. Такая ситуация еще может быть приемлема для цепей сброса, но для цепей разрешения это недопустимо. Пример такой ситуации приведен ниже (Рис.6.29). В данном VHDL примере коэффициент разветвления сигнала разрешения "Tri_en" равен 24. Рис.6.29. architecture load of four_load is signal Tri_en std_logic; begin loadpro: process (Clk) begin if (clk'event and clk ='1') then Tri_end <= Tri_en; end if; end process loadpro; endpro : process (Tri_end, Data_in) begin if (Tri_end = '1') then out <= Data_in; else out <= (others => 'Z'); end if; end process endpro; end load; Число разветвлений (fanout) можно уменьшить вдвое, используя два регистра (Рис.6.30) Рис.6.30 Ниже приведен пример реализации дублирования регистров на VHDL. architecture loada of two_load is signal Tri_en1, Tri_en2 : std_logic; begin loadpro: process (Clk) begin if (clk'event and clk ='1') then Tri_en1 <= Tri_en; Tri_en2 <= Tri_en; end if; end process loadpro; process (Tri_en1, Data_in) begin if (Tri_en1 = '1') then out(23:12) <= Data_in(23:12); else out(23:12) <= (others => 'Z'); end if; end process; process (Tri_en2, Data_in) begin if (Tri_en2 = '1') then out(11:0) <= Data_in(11:0); else out(11:0) <= (others => 'Z'); end if; end process; Разделение проекта на части оптимальных размеров позволяет обеспечить оптимальное использование ресурсов за счет их оптимизации при синтезе. Известно, что подавляющее большинство программ синтеза достигают наиболее приемлимого результата при размере блока порядка 2-5 тысяч эквивалентных вентилей. Приведенный ниже пример показывает как можно сэкономить ресурсы путем замены встроенного в модуль регистра(Рис.6.31) на выделенный регистр (рис 3.32) Рис.6.31 process (clk, a, b) begin if (clk'event and clk = '1') then a1 <= a; b1 <=b; end if; end process; process (a1, b1) begin c <=a1 +b1; end process; Рис.6.32 process (clk, a, b) begin if (clk'event and clk = '1') then c <=a +b; end if; end process; 6.13.Создание описаний с учетом особенностей архитектуры ПЛИС (Technology Specific Coding Techniques). Помимо способов создания описаний, независимых от используемой элементной базы и ее архитектурных особенностей, в практике проектирования устройств на ПЛИС применяется так называемое создание описаний с учетом особенностей архитектуры ПЛИС (Technology Specific Coding Techniques). Рассмотрим некоторые примеры проектирования цифровых устройств для использования с учетом особенностей архитектуры FPGA ПЛИС. Для реализации мультиплексоров как правило применяется конструкция case, что значительно выгоднее с точки зрения быстродействия и затрат ресурсов, чем конструкция if-then-else. Большинство производителей ПЛИС рекоммендуют использовать оператор case. Как правило, при реализации на ПЛИС внутренняя шина с тремя состояниями эмулируется как мультиплексор. Пример эмуляции шины в виде мультиплексора приведен на Рис.6.33 Рис.6.33 Пример эмуляции шины на VHDL library IEEE; use IEEE.std_logic_1164.all; entity tribus is port (A, B, C, D : in std_logic_vector(7 downto 0); E0, E1, E2, E3 : in std_logic; Q :out std_logic_vector(7 downto 0)); end tribus; architecture rtl of tribus is begin Q <=Awhen(E0 = '1') else "ZZZZZZZZ"; Q <=Bwhen(E1 = '1') else "ZZZZZZZZ"; Q <=Cwhen(E2 = '1') else "ZZZZZZZZ"; Q <=Dwhen(E3 = '1') else "ZZZZZZZZ"; end rtl; Мультиплексор на VHDL library IEEE; use IEEE.std_logic_1164.all; entity muxbus is port (A, B, C, D : in std_logic_vector(7 downto 0); E0, E1, E2, E3 : in std_logic; Q :out std_logic_vector(7 downto 0)); end muxbus; architecture rtl of muxbus is signal E_int : std_logic_vector(1 downto 0); begin process (E0, E1, E2, E3) variable E : std_logic_vector(3 downto 0); begin E :=E0 &E1 &E2 &E3; case E is when "0001" => E_int <= "00"; when "0010" => E_int <= "01"; when "0100" => E_int <= "10"; when "1000" => E_int <= "11"; when others => E_int <= "--"; end case; end process; process (E_int, A, B, C, D) begin case E_int is when "00" => Q <= D; when "01" => Q <= C; when "10" => Q <= B; when "11" => Q <= A; when others =>Q <=(others => '-'); end case; end process; end rtl; Эмуляция шины на Verilog module tribus (A, B, C, D, E0, E1, E2, E3, Q); input [7:0]A, B, C, D; output [7:0]Q; input E0, E1, E2, E3; assign Q[7:0] = E0 ? A[7:0] : 8'bzzzzzzzz; assign Q[7:0] = E1 ? B[7:0] : 8'bzzzzzzzz; assign Q[7:0] = E2 ? C[7:0] : 8'bzzzzzzzz; assign Q[7:0] = E3 ? D[7:0] : 8'bzzzzzzzz; endmodule Реализация мультиплексора на Verilog module muxbus (A, B, C, D, E0, E1, E2, E3, Q); input [7:0]A, B, C, D; output [7:0]Q; input E0, E1, E2, E3; wire [3:0] select4; reg [1:0] select2; reg [7:0]Q; assign select4 = {E0, E1, E2, E3}; always @ (select4) begin case(select4) 4'b0001 : select2 = 2'b00; 4'b0010 : select2 = 2'b01; 4'b0100 : select2 = 2'b10; 4'b1000 : select2 = 2'b11; default : select2 = 2'bxx; endcase end always @ (select2 or A or B or C or D) begin case(select2) 2'b00 :Q =D; 2'b01 :Q =C; 2'b10 :Q =B; 2'b11 :Q =A; endcase end endmodule Несколько примеров хотелось бы привести по реализации устройств памяти Приведенный ниже пример иллюстрирует описание статической памяти (SRAM) на VHDL для реализации на ПЛИС, не имеющих встроенных блоков памяти. В ряде случаев этот прием позволяет не выходя за рамки одного устройствареализовать небольшой буфер. Пример реализован на VHDL -- ************************************************* -- Behavioral description of a single-port SRAM with: -- Active High write enable (WE) -- Rising clock edge (Clock) -- ************************************************* library ieee; use ieee.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity reg_sram is generic (width : integer:=8; depth : integer:=8; addr : integer:=3); port (Data : in std_logic_vector (width-1 downto 0); Q :out std_logic_vector (width-1 downto 0); Clock : in std_logic; WE : in std_logic; Address : in std_logic_vector (addr-1 downto 0)); end reg_sram; architecture behav of reg_sram is type MEM is array (0 to depth-1) of std_logic_vector(width-1 downto 0); signal ramTmp : MEM; begin process (Clock) begin if (clock'event and clock='1') then if (WE = '1') then ramTmp (conv_integer (Address)) <= Data; end if; end if; end process; Q <= ramTmp(conv_integer(Address)); end behav; Модель на Verilog имеет вид `timescale 1 ns/100 ps //######################################################## //# Behavioral single-port SRAM description : //# Active High write enable (WE) //# Rising clock edge (Clock) //####################################################### module reg_sram (Data, Q, Clock, WE, Address); parameter width = 8; parameter depth = 8; parameter addr = 3; input Clock, WE; input [addr-1:0] Address; input [width-1:0] Data; output [width-1:0] Q; wire [width-1:0] Q; reg [width-1:0] mem_data [depth-1:0]; always @(posedge Clock) if(WE) mem_data[Address] = #1 Data; assign Q = mem_data[Address]; endmodule Следующий пример иллюстрирует создание модели двупортовой статической памяти (Dual-Port SRAM). Описывается модуль 8 х 8 ячеек. Описание на VHDL имеет вид -- Behavioral description of dual-port SRAM with : -- Active High write enable (WE) -- Active High read enable (RE) -- Rising clock edge (Clock) library ieee; use ieee.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity reg_dpram is generic (width : integer:=8; depth : integer:=8; addr : integer:=3); port (Data : in std_logic_vector (width-1 downto 0); Q :out std_logic_vector (width-1 downto 0); Clock : in std_logic; WE : in std_logic; RE : in std_logic; WAddress: in std_logic_vector (addr-1 downto 0); RAddress: in std_logic_vector (addr-1 downto 0)); end reg_dpram; architecture behav of reg_dpram is type MEM is array (0 to depth-1) of std_logic_vector(width-1 downto 0); signal ramTmp : MEM; begin -- Write Functional Section process (Clock) begin if (clock'event and clock='1') then if (WE = '1') then ramTmp (conv_integer (WAddress)) <= Data; end if; end if; end process; -- Read Functional Section process (Clock) begin if (clock'event and clock='1') then if (RE = '1') then Q <= ramTmp(conv_integer (RAddress)); end if; end if; end process; end behav; Модель двупортовой памяти на Verilog `timescale 1 ns/100 ps //######################################################## //# Behavioral dual-port SRAM description : //# Active High write enable (WE) //# Active High read enable (RE) //# Rising clock edge (Clock) //####################################################### module reg_dpram (Data, Q, Clock, WE, RE, WAddress, RAddress); parameter width = 8; parameter depth = 8; parameter addr = 3; input Clock, WE, RE; input [addr-1:0] WAddress, RAddress; input [width-1:0] Data; output [width-1:0] Q; reg [width-1:0] Q; reg [width-1:0] mem_data [depth-1:0]; // ###################################################### // # Write Functional Section // ###################################################### always @(posedge Clock) begin if(WE) mem_data[WAddress] = #1 Data; end //#################################################### //# Read Functional Section //#################################################### always @(posedge Clock) begin if(RE) Q = #1 mem_data[RAddress]; end endmodule