Elimizde M25P128 Flash entegresine bilgisayarın seri portu üzerinden veri yazmak ve okumak amacıyla
tasarlanmış Şekil-1’deki gibi bir kart olduğunu düşünelim. Bu ödevde Şekil-1’de gösterilen SPI Flash
okuma kartı için, bilgisayarın seri portu üzerinden gelen komut ile M25P128 SPI Flash entegresinden
okuma yapabilen bir FPGA tasarımı yapınız. (Yazma yok)
M25P128 Entegresinin veri sayfasını aşağıdaki adresten indirebilirsiniz.
https://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p128.pdf
Bilgisayardan gelen 4 byte okuma komutu şu şekilde olacaktır: |AA + 24 bit address|
Bu komut seri arayüzden gönderildikten sonra okuma işlemi yapılacak ve tek bir byte UART arayüzü
üzerinden bilgisayara gönderilcektir.
a) Ön tasarımı akış diyagramı şu şekildedir
Bize ayrıca M25P128 entegresi gibi sinyal gönderecek bir modül gereklidir.
entity M25P128 is Port ( S : in STD_LOGIC; C : in STD_LOGIC; D : in STD_LOGIC; Q : out STD_LOGIC := 'Z'); end M25P128; architecture Behavioral of M25P128 is signal counter : integer := 0; signal junkData : STD_LOGIC := '0'; begin process (C,S) begin if rising_edge(C) and S = '0' then if (counter < 32) then counter <= counter + 1; end if; end if; if falling_edge(C) and S = '0' then if (counter = 32) then junkData <= not(junkData); Q <= junkData; end if; end if; if (S = '1') then counter <= 0; Q <= 'Z'; end if; end process; end Behavioral;
Modül için testbench kodu
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity tb_M25P128 is -- Port ( ); end tb_M25P128; architecture Behavioral of tb_M25P128 is component M25P128 is Port ( S : in STD_LOGIC; C : in STD_LOGIC; D : in STD_LOGIC; Q : out STD_LOGIC); end component; signal S,C,D,Q : STD_LOGIC; constant period : time := 10ns; begin MP : M25P128 port map(S => S, C => C, D => D, Q => Q); process begin S <= '1'; D <= '0'; wait for 3*period; S <= '0'; wait for 50*period; S <= '1'; wait for 10*period; S <= '0'; wait; end process; process begin C <= '0'; wait for period/2; C <= '1'; wait for period/2; end process; end Behavioral;
Simülasyon sonucu
b) UART arayüzü üzerinden haberleşme için gerekli kodu yazarak benzetimini yapalım.
-- Eric Bainville -- Mar 2013 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.math_real.all; entity uart is generic ( DIVISOR: natural -- DIVISOR = 100,000,000 / (16 x BAUD_RATE) -- 2400 -> 2604 -- 9600 -> 651 -- 115200 -> 54 -- 1562500 -> 4 -- 2083333 -> 3 ); port ( clk: in std_logic; -- clock reset: in std_logic; -- reset -- Client interface rx_data: out std_logic_vector(7 downto 0); -- received byte rx_enable: out std_logic; -- validates received byte (1 system clock spike) tx_data: in std_logic_vector(7 downto 0); -- byte to send tx_enable: in std_logic; -- validates byte to send if tx_ready is '1' tx_ready: out std_logic; -- if '1', we can send a new byte, otherwise we won't take it -- Physical interface rx: in std_logic; tx: out std_logic ); end uart; architecture Behavioral of uart is constant COUNTER_BITS : natural := integer(ceil(log2(real(DIVISOR)))); type fsm_state_t is (idle, active); -- common to both RX and TX FSM type rx_state_t is record fsm_state: fsm_state_t; -- FSM state counter: std_logic_vector(3 downto 0); -- tick count bits: std_logic_vector(7 downto 0); -- received bits nbits: std_logic_vector(3 downto 0); -- number of received bits (includes start bit) enable: std_logic; -- signal we received a new byte end record; type tx_state_t is record fsm_state: fsm_state_t; -- FSM state counter: std_logic_vector(3 downto 0); -- tick count bits: std_logic_vector(8 downto 0); -- bits to emit, includes start bit nbits: std_logic_vector(3 downto 0); -- number of bits left to send ready: std_logic; -- signal we are accepting a new byte end record; signal rx_state,rx_state_next: rx_state_t; signal tx_state,tx_state_next: tx_state_t; signal sample: std_logic; -- 1 clk spike at 16x baud rate signal sample_counter: std_logic_vector(COUNTER_BITS-1 downto 0) := (others => '0'); -- should fit values in 0..DIVISOR-1 begin -- sample signal at 16x baud rate, 1 CLK spikes sample_process: process (clk,reset) is begin if reset = '1' then sample_counter <= (others => '0'); sample <= '0'; elsif rising_edge(clk) then if sample_counter = DIVISOR-1 then sample <= '1'; sample_counter <= (others => '0'); else sample <= '0'; sample_counter <= sample_counter + 1; end if; end if; end process; -- RX, TX state registers update at each CLK, and RESET reg_process: process (clk,reset) is begin if reset = '1' then rx_state.fsm_state <= idle; rx_state.bits <= (others => '0'); rx_state.nbits <= (others => '0'); rx_state.enable <= '0'; tx_state.fsm_state <= idle; tx_state.bits <= (others => '1'); tx_state.nbits <= (others => '0'); tx_state.ready <= '1'; elsif rising_edge(clk) then rx_state <= rx_state_next; tx_state <= tx_state_next; end if; end process; -- RX FSM rx_process: process (rx_state,sample,rx) is begin case rx_state.fsm_state is when idle => rx_state_next.counter <= (others => '0'); rx_state_next.bits <= (others => '0'); rx_state_next.nbits <= (others => '0'); rx_state_next.enable <= '0'; if rx = '0' then -- start a new byte rx_state_next.fsm_state <= active; else -- keep idle rx_state_next.fsm_state <= idle; end if; when active => rx_state_next <= rx_state; if sample = '1' then if rx_state.counter = 8 then -- sample next RX bit (at the middle of the counter cycle) if rx_state.nbits = 9 then rx_state_next.fsm_state <= idle; -- back to idle state to wait for next start bit rx_state_next.enable <= rx; -- OK if stop bit is '1' else rx_state_next.bits <= rx & rx_state.bits(7 downto 1); rx_state_next.nbits <= rx_state.nbits + 1; end if; end if; rx_state_next.counter <= rx_state.counter + 1; end if; end case; end process; -- RX output rx_output: process (rx_state) is begin rx_enable <= rx_state.enable; rx_data <= rx_state.bits; end process; -- TX FSM tx_process: process (tx_state,sample,tx_enable,tx_data) is begin case tx_state.fsm_state is when idle => if tx_enable = '1' then -- start a new bit tx_state_next.bits <= tx_data & '0'; -- data & start tx_state_next.nbits <= "0000" + 10; -- send 10 bits (includes '1' stop bit) tx_state_next.counter <= (others => '0'); tx_state_next.fsm_state <= active; tx_state_next.ready <= '0'; else -- keep idle tx_state_next.bits <= (others => '1'); tx_state_next.nbits <= (others => '0'); tx_state_next.counter <= (others => '0'); tx_state_next.fsm_state <= idle; tx_state_next.ready <= '1'; end if; when active => tx_state_next <= tx_state; if sample = '1' then if tx_state.counter = 15 then -- send next bit if tx_state.nbits = 0 then -- turn idle tx_state_next.bits <= (others => '1'); tx_state_next.nbits <= (others => '0'); tx_state_next.counter <= (others => '0'); tx_state_next.fsm_state <= idle; tx_state_next.ready <= '1'; else tx_state_next.bits <= '1' & tx_state.bits(8 downto 1); tx_state_next.nbits <= tx_state.nbits - 1; end if; end if; tx_state_next.counter <= tx_state.counter + 1; end if; end case; end process; -- TX output tx_output: process (tx_state) is begin tx_ready <= tx_state.ready; tx <= tx_state.bits(0); end process; end Behavioral;
UART için testbench kodu
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity tb_UART is -- Port ( ); end tb_UART; architecture Behavioral of tb_UART is component uart is generic ( DIVISOR: natural ); port ( clk: in std_logic; -- system clock reset: in std_logic; -- Client interface rx_data: out std_logic_vector(7 downto 0); -- received byte rx_enable: out std_logic; -- validates received byte (1 system clock spike) tx_data: in std_logic_vector(7 downto 0); -- byte to send tx_enable: in std_logic; -- validates byte to send if tx_ready is '1' tx_ready: out std_logic; -- if '1', we can send a new byte, otherwise we won't take it -- Physical interface rx: in std_logic; tx: out std_logic ); end component; signal CLK, uartRST, RXen, TXen, TXrdy, RX, TX : STD_LOGIC; signal RXdata, TXdata : STD_LOGIC_VECTOR(7 downto 0); constant period : time := 10ns; -- 100 MHz signal lowCLK : STD_LOGIC; begin UART0 : uart generic map (DIVISOR => 54) -- 115200 port map ( clk => CLK, reset => uartRST, rx_data => RXdata, rx_enable => RXen, tx_data => TXdata, tx_enable => TXen, tx_ready => TXrdy, rx => RX, tx => TX ); process begin CLK <= '0'; wait for period/2; CLK <= '1'; wait for period/2; end process; process begin lowCLK <= '0'; wait for 864*period/2; lowCLK <= '1'; wait for 864*period/2; end process; process begin uartRST <= '1'; wait for 10*period; uartRST <= '0'; wait; end process; process begin TXen <= '0'; TXdata <= (others => '0'); wait for 864*period; TXdata <= X"AA"; TXen <= '1'; wait for 1*period; TXdata <= X"00"; TXen <= '0'; wait until TXrdy = '1'; TXdata <= X"FF"; TXen <= '1'; wait for 1*period; TXdata <= X"00"; TXen <= '0'; wait; end process; RX <= TX; end Behavioral;
UART Modülü için benzetim sonuçları, burada TX sinyali devreye giriş olarak geri verildiği için RX sinyalinde de doğru oluştuğu gözlenebilmektedir.
c) M25P128 entegresini SPI arayüzü üzerinden okuyan ve diğer modülleri içeren ana modülü yazalım.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ZC0720FPGA is Port ( CLK : in STD_LOGIC; RX : in STD_LOGIC; TX : out STD_LOGIC := '0'); end ZC0720FPGA; architecture Behavioral of ZC0720FPGA is component uart is generic ( DIVISOR: natural ); port ( clk: in std_logic; -- system clock reset: in std_logic; -- Client interface rx_data: out std_logic_vector(7 downto 0); -- received byte rx_enable: out std_logic; -- validates received byte (1 system clock spike) tx_data: in std_logic_vector(7 downto 0); -- byte to send tx_enable: in std_logic; -- validates byte to send if tx_ready is '1' tx_ready: out std_logic; -- if '1', we can send a new byte, otherwise we won't take it -- Physical interface rx: in std_logic; tx: out std_logic ); end component; component M25P128 is Port ( S : in STD_LOGIC; C : in STD_LOGIC; D : in STD_LOGIC; Q : out STD_LOGIC); end component; signal Dcounter : integer := 31; signal Qcounter : STD_LOGIC_VECTOR(4 downto 0) := (others => '0'); signal data : STD_LOGIC_VECTOR(7 downto 0) := (others => '0'); signal command : STD_LOGIC_VECTOR(31 downto 0) := (others => '0'); signal commandReady : STD_LOGIC := '0'; signal RXcounter : STD_LOGIC_VECTOR(3 downto 0) := (others => '0'); type fsmState is (readRX, writeD, readQ, writeTX); signal fsmStatus, fsmNextStatus : fsmState; signal uartRST, RXen, TXen, TXrdy : STD_LOGIC := '0'; signal RXdata, TXdata : STD_LOGIC_VECTOR(7 downto 0) := (others => '0'); signal Q : STD_LOGIC; signal S, D : STD_LOGIC := '0'; begin UART0 : uart generic map (DIVISOR => 54) -- 50,000,000 / 115200 / 16 port map ( clk => CLK, reset => uartRST, rx_data => RXdata, rx_enable => RXen, tx_data => TXdata, tx_enable => TXen, tx_ready => TXrdy, rx => RX, tx => TX ); MP : M25P128 port map(S => S, C => CLK, D => D, Q => Q); process (fsmStatus) begin case fsmStatus is when readRX => S <= '1'; when writeD => S <= '0'; when readQ => S <= '0'; when writeTX => S <= '1'; when others => null; end case; end process; process (CLK) begin if falling_edge(CLK) then if fsmStatus = writeD then if (Dcounter > 0) then D <= command(Dcounter); Dcounter <= Dcounter - 1; elsif (Dcounter = 0) then fsmNextStatus <= readQ; Dcounter <= 31; end if; end if; elsif rising_edge(CLK) then if fsmStatus = readRX then TXen <= '0'; commandReady <= '0'; if RXen = '1' then RXcounter <= RXcounter + '1'; command(31 downto 0) <= command(23 downto 0) & RXdata(0) & RXdata(1) & RXdata(2) & RXdata(3) & RXdata(4) & RXdata(5) & RXdata(6) & RXdata(7); end if; if (RXcounter = "0100") then RXcounter <= "0000"; commandReady <= '1'; end if; if (command(31 downto 24) = X"AA") and commandReady = '1' then fsmNextStatus <= writeD; end if; end if; if fsmStatus = readQ then if (Qcounter < 8) then TXdata(7 downto 0) <= TXdata(6 downto 0) & Q; Qcounter <= Qcounter + 1; elsif (Qcounter = 8) then fsmNextStatus <= writeTX; end if; end if; if fsmStatus = writeTX and TXrdy = '1' then TXen <= '1'; fsmNextStatus <= readRX; end if; end if; end process; process (CLK) begin fsmStatus <= fsmNextStatus; end process; end Behavioral;
d) Projeyi bütün halinde test etmek için bir testbench yazarak XSIM ile benzetimini yapalım.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity tb_FPGA is -- Port ( ); end tb_FPGA; architecture Behavioral of tb_FPGA is component ZC0720FPGA is Port ( CLK : in STD_LOGIC; RX : in STD_LOGIC; TX : out STD_LOGIC); end component; constant period : time := 20ns; -- 50 MHz signal CLK, RX, TX : STD_LOGIC; begin FPGA: ZC0720FPGA port map( CLK => CLK, RX => RX, TX => TX); process begin CLK <= '0'; wait for period/2; CLK <= '1'; wait for period/2; end process; process begin RX <= '1'; wait for 864*period; RX <= '0'; -- Start wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; -- Stop wait for 864*period; RX <= '1'; wait for 100*period; RX <= '0'; -- Start wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; -- Stop wait for 864*period; RX <= '1'; wait for 100*period; RX <= '0'; -- Start wait for 864*period; RX <= '0'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; -- Stop wait for 864*period; RX <= '1'; wait for 100*period; RX <= '0'; -- Start wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '0'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; wait for 864*period; RX <= '1'; -- Stop wait for 864*period; RX <= '1'; wait for 16*period; wait; end process; end Behavioral;
Benzetim sonuçları