Simple AXI Slave

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA project was completed in January 2017.

I found getting started with the Zynq at the HDL level really hard - If I used IP blocks and automation it is relatively easy to make systems, but you can't do anything that is outside of the box. Also, IP blocks are relatively expensive in terms of FPGA resources.

After reading the AXI bus spec I wrote my own slave interface, and hooked it up to the simple I/O on Pynq Dev Board.

It works! The buttons, LEDs and RGB LEDs are now under software control.

Contents

The registers implemented

As written, 16 32-bit registers can be implemented. Because I don't decode all the address they repeat for the entire window assigned to the AXI interface. Here is the registers I configured for this design:

Offset Read function Write Function
0 [3:0] LEDs values [3:0] LEDs values
1 [3:0] buttons None
2 [2:0] RGB0, [6:4] RGB1 Current setting for RGB LEDs
3 - 15 None (reads as 1s) None

The block design for the Zynq

The block-level design is pretty standard, with the AXI bus exported to the HDL design. It was build by applying the customization script from https://reference.digilentinc.com/_media/reference/programmable-logic/pynq-z1/pynq_revc.zip

Axi block design.png

Axi addresses.png

I should really also add a port for the reset signal and make use of it in the AXI_slave...

FPGA Utilization

As you can see, utilization is quite low. I'll do a comparison with a IP-based GPIO design sometime....

AXI utilization.png

The HDL Code

reg_file.vhd

---------------------------------------------------------------
-- reg_file.vhd : A simple 16x32-bit register file
--
-- Author: Mike Field <hamster@snap.net.nz>
--
-- Note the data_out lookup is not clocked!
-----------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity reg_file is
    Port ( clk      : in STD_LOGIC;
           addr     : in STD_LOGIC_VECTOR (3 downto 0);
           data_in  : in STD_LOGIC_VECTOR (31 downto 0);
           we       : in STD_LOGIC_VECTOR (3 downto 0);
           data_out : out STD_LOGIC_VECTOR (31 downto 0);
           
           leds     : out STD_LOGIC_VECTOR (3 downto 0);
           rgb0     : out STD_LOGIC_VECTOR (2 downto 0);
           rgb1     : out STD_LOGIC_VECTOR (2 downto 0);
           buttons  : in  STD_LOGIC_VECTOR (3 downto 0)
           );
end reg_file;

architecture Behavioral of reg_file is
    type t_reg is array (0 to 15) of STD_LOGIC_VECTOR (31 downto 0);
    
    -- Registers are split into bytes for byte-masked writes.
    signal reg : t_reg := (others => (others => '0')); 
begin
    ----------------------------------------------------
    -- Pass the register values out to the outside world
    ----------------------------------------------------
   leds <= reg(0)(3 downto 0);
   rgb0 <= reg(2)(2 downto 0);
   rgb1 <= reg(2)(6 downto 4);
   
   -------------------------------------------------------
   -- Mux the 'read' values together, 
   -- Make sure unwanted bits read back as zeros, rather 
   -- than reg(n) value, so that they can be optimized out.
   -- Note - HAS TO BE COMBINATORIAL, NOT CLOCKED!
   -------------------------------------------------------
read_proc: process(addr,reg)
    begin
        -- Default data out to zeros
        data_out <= (others => '0');
        case addr is
            -- Set the bits in any used registers
            when "0000" => data_out(3 downto 0) <= reg(0)(3 downto 0);
            when "0001" => data_out(3 downto 0) <= buttons (3 downto 0);
            when "0010" => data_out(2 downto 0) <= reg(2)(2 downto 0);
                           data_out(6 downto 4) <= reg(2)(6 downto 4);
            -- Any undecoded registers will read back as all '1's
            when others => data_out <= (others => '1');  
        end case;
    end process;

   -------------------------------------------------------
   -- Write any incoming values into the register array
   -- respecting the per-byte write strobes.
   -------------------------------------------------------
write_proc: process(clk)
    begin
        if rising_edge(clk) then
            -- Update any bytes that are written
            if we(0) = '1' then
                reg(to_integer(unsigned(addr)))( 7 downto 0) <= data_in( 7 downto  0);
            end if;

            if we(1) = '1' then
                reg(to_integer(unsigned(addr)))(15 downto  8) <= data_in(15 downto  8);
            end if;

            if we(2) = '1' then
                reg(to_integer(unsigned(addr)))(23 downto 16) <= data_in(23 downto 16);
            end if;

            if we(3) = '1' then
                reg(to_integer(unsigned(addr)))(31 downto 24) <= data_in(31 downto 24);
            end if;
        end if;
    end process;

end Behavioral;

my_axi_slave.vhd

Here it the core of the design. It processes AXI reads and writes, and attempts to make sense of the AXI burst modes. Oh, I haven't implemented the required reset, that will place the FSM into "s_idle" state. If you use this, you really should add it!

It most probably has plenty of other errors, and isn't the fastest implementation ever, but it seems to work just fine.

-------------------------------------------------------------
-- my_axi_slave.vhd
--
-- Author: Mike Field <hamster@snap.net.nz>
--
-- My own AXI slave implementation, to see what is involved.
-- connects the AXI bus to a register file of 16 registers.
-------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity my_axi_slave is
  port (
    ------------------------
    -- Incoming system clock
    ------------------------
    clk         : in  STD_LOGIC;
    
    ------------------------
    -- AXI bus interface clock
    ------------------------
    AXI_ACLK    : out STD_LOGIC := '0';    

    ------------------------
    -- Write address channel    
    ------------------------
    AXI_awvalid : in  STD_LOGIC;    
    AXI_awid    : in  STD_LOGIC_VECTOR (11 downto 0 );
    AXI_awburst : in  STD_LOGIC_VECTOR ( 1 downto 0 );
    AXI_awlock  : in  STD_LOGIC_VECTOR ( 1 downto 0 );
    AXI_awsize  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
    AXI_awprot  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
    AXI_awaddr  : in  STD_LOGIC_VECTOR (31 downto 0 );
    AXI_awcache : in  STD_LOGIC_VECTOR ( 3 downto 0 );
    AXI_awlen   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
    AXI_awqos   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
    AXI_awready : out STD_LOGIC := '0';

    ------------------------
    -- Write Data channel    
    ------------------------
    AXI_wdata   : in  STD_LOGIC_VECTOR (31 downto 0 );
    AXI_wstrb   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
    AXI_wready  : out STD_LOGIC := '0';
    AXI_wlast   : in  STD_LOGIC;
    AXI_wvalid  : in  STD_LOGIC;
    AXI_wid     : in  STD_LOGIC_VECTOR (11 downto 0 );

    ------------------------
    -- Read address channel
    ------------------------
    AXI_arvalid : in  STD_LOGIC;
    AXI_arready : out STD_LOGIC := '0';
    AXI_arid    : in  STD_LOGIC_VECTOR ( 11 downto 0 );
    AXI_arburst : in  STD_LOGIC_VECTOR ( 1 downto 0 );
    AXI_arlock  : in  STD_LOGIC_VECTOR ( 1 downto 0 );
    AXI_arsize  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
    AXI_arprot  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
    AXI_araddr  : in  STD_LOGIC_VECTOR (31 downto 0 );
    AXI_arcache : in  STD_LOGIC_VECTOR ( 3 downto 0 );
    AXI_arlen   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
    AXI_arqos   : in  STD_LOGIC_VECTOR ( 3 downto 0 );

    ------------------------
    -- Read data channel
    ------------------------
    AXI_rready  : in  STD_LOGIC;
    AXI_rlast   : out STD_LOGIC := '0';
    AXI_rvalid  : out STD_LOGIC := '0';
    AXI_rid     : out STD_LOGIC_VECTOR (11 downto 0 ) := (others => '0');
    AXI_rresp   : out STD_LOGIC_VECTOR ( 1 downto 0 ) := (others => '0');
    AXI_rdata   : out STD_LOGIC_VECTOR (31 downto 0 ) := (others => '0');

    ------------------------
    -- Write status channel 
    ------------------------
    AXI_bready  : in  STD_LOGIC;
    AXI_bvalid  : out STD_LOGIC := '0';
    AXI_bid     : out STD_LOGIC_VECTOR (11 downto 0 ) := (others => '0');
    AXI_bresp   : out STD_LOGIC_VECTOR ( 1 downto 0 ) := (others => '0');

    ---------------------------------
    -- Register file interface
    ---------------------------------
    reg_address      : out std_logic_vector ( 3 downto 0 ) := (others => '0');
    reg_write_data   : out std_logic_vector (31 downto 0 ) := (others => '0');
    reg_write_strobe : out std_logic_vector ( 3 downto 0 ) := (others => '0');
    reg_read_data    : in  std_logic_vector (31 downto 0 ) := (others => '0')
    );
end my_axi_slave;

architecture Behavioral of my_axi_slave is
    type t_state is (s_idle, s_read, s_write, s_write_wait);
    signal state                   : t_state := s_idle;

    signal words_to_go        : unsigned(3 downto 0) := (others => '0');
    signal write_id           : STD_LOGIC_VECTOR (11 downto 0 );
    signal address_start      : STD_LOGIC_VECTOR (31 downto 0 ) := (others => '0');
    signal address_incr       : STD_LOGIC_VECTOR (31 downto 0 ) := (others => '0');
    signal address_mask       : STD_LOGIC_VECTOR ( 7 downto 0 ) := (others => '1');
    signal address            : STD_LOGIC_VECTOR (31 downto 0 ) := (others => '0');
begin
    AXI_ACLK         <= clk;    

calc_addr_proc: process(address_mask, address_start, address_incr)
    begin
        -- Bit 7 and up are selected based on bit 7 of addreess_mask
        -- If set, it uses bits from the the start address, 
        -- If not, it uses the incrementing address
        address <= address_start;
        if address_start(7) = '1' then
            address(address'high downto 7) <= address_start(address'high downto 7);
        else
            address(address'high downto 7) <= address_incr(address'high downto 7);
        end if;

        -- Bit 0 to six are selected based on the low 7 bits of addreess_mask
        -- If set, it uses bits from the the start address, 
        -- If not, it uses the incrementing address        
        address(6 downto 0) <= (address_start(6 downto 0) AND     address_mask(6 downto 0))
                            OR (address_incr (6 downto 0) AND NOT address_mask(6 downto 0));
    end process;

map_through_writes_proc: process(state, AXI_wvalid)
    begin
        if state = s_write and AXI_wvalid = '1' then
            reg_write_strobe <= AXI_wstrb;
        else
            reg_write_strobe <= (others => '0');
        end if;
    end process;
    reg_address      <= address(5 downto 2);
    reg_write_data   <= AXI_wdata; 
    AXI_rdata        <= reg_read_data;
    
assert_aXready_proc: process(AXI_awvalid, AXI_arvalid, state)
    begin
        AXI_awready <= '0';
        AXI_arready <= '0';
        if state = s_idle then
            AXI_awready <= AXI_awvalid;
            AXI_arready <= AXI_arvalid AND NOT AXI_awvalid;
        end if;
    end process;
     
clk_proc: process(clk)
    begin
        if rising_edge(clk) then     
            case state is 
                when s_idle =>
                    AXI_rvalid  <= '0';
                    AXI_wready  <= '0';
                    if AXI_awvalid = '1' then
                        state       <= s_write;
                        -- Receive write transaction, 
                        address_start <= AXI_awaddr; 
                        address_incr  <= AXI_awaddr; 
                        case AXI_awburst is
                           when "00"   => -- Fixed
                                address_mask    <= x"FF"; 
                           when "01"   => -- Incr
                                address_mask    <= x"00"; 
                           when "10"   =>  -- Wrap 
                                case AXI_awsize is
                                    when "000"  => address_mask    <= x"FF";
                                    when "001"  => address_mask    <= x"FE"; 
                                    when "010"  => address_mask    <= x"FC"; 
                                    when "011"  => address_mask    <= x"F8"; 
                                    when "100"  => address_mask    <= x"F0"; 
                                    when "101"  => address_mask    <= x"E0"; 
                                    when "110"  => address_mask    <= x"C0"; 
                                    when others => address_mask    <= x"80"; 
                                end case;
                           when others => -- Fixed
                                address_mask    <= x"FF"; 
                        end case;

                        -- NOTE: We ignore AXI_awlen, and use the last flag on the data transfer                        
                        -- Remember Transaction Id and set the response code for the write ack 
                         AXI_bid     <= AXI_awid;
                         if AXI_awlock = "01" then
                             AXI_bresp <= "01";  -- EXOKAY response code
                         else
                             AXI_bresp <= "00";  -- OKAY response code
                         end if;
                         
                         -- Set up to receive data                         
                         AXI_wready  <= '1';
                         
                    elsif AXI_arvalid = '1' then
                         -- Service read transaction
                         -- remember how large the transfer will be
                         address_start <= AXI_araddr; 
                         address_incr  <= AXI_araddr;
                          
                         case AXI_arburst is
                            when "00"   => -- Fixed
                                 address_mask    <= x"FF"; 
                            when "01"   => -- Incr
                                 address_mask    <= x"00"; 
                            when "10"   =>  -- Wrap 
                                 case AXI_arsize is
                                     when "000"  => address_mask    <= x"FF";
                                     when "001"  => address_mask    <= x"FE"; 
                                     when "010"  => address_mask    <= x"FC"; 
                                     when "011"  => address_mask    <= x"F8"; 
                                     when "100"  => address_mask    <= x"F0"; 
                                     when "101"  => address_mask    <= x"E0"; 
                                     when "110"  => address_mask    <= x"C0"; 
                                     when others => address_mask    <= x"80"; 
                                 end case;
                            when others => -- Fixed
                                 address_mask    <= x"FF"; 
                         end case;
                         words_to_go <= unsigned(AXI_arlen);
                        
                        -- remember Transaction Id for the response 
                         AXI_rid     <= AXI_arid;

                         -- Set the response code             
                         IF AXI_arlock = "01" then
                             AXI_rresp   <= "01"; -- EXOKAY
                         else
                             AXI_rresp   <= "00"; -- OKAY
                         end if;  
                                             
                         AXI_rvalid  <= '1';
                         state <= s_read;                    
                         if unsigned(AXI_arlen) = 0 then
                            AXI_rlast <= '1';
                         end if;
                    end if;
                when s_write =>
                    ------------------------------------------
                    -- Is the Master transmitting a data word?
                    ------------------------------------------
                    if AXI_wvalid = '1' then
                        ---------------------------------------------------
                        -- Is this the last word of the burst? 
                        -- If so, send the response in the AXI_b channel 
                       --------------------------------------------
                        if AXI_wlast = '1' then
                            -- AXI_bresp is already set!
                            AXI_bvalid <= '1';
                            AXI_wready  <= '0';
                            state     <= s_write_wait;
                        end if;
                        address_incr <= std_logic_vector(unsigned(address_incr) + 1);                                                 
                   end if;
                    
                when s_write_wait => 
                    -------------------------------------------------------
                    -- Wait for AXI_bready to be asserted before going idle
                    -- (we have already asserted 'AXI_bvalid')
                    -------------------------------------------------------
                    if AXI_bready = '1' then
                        AXI_bvalid <= '0';
                        state <= s_idle;
                    end if;

                when s_read =>
                    -------------------------------------------------------------
                    -- Service the read until we get to the last word to transfer
                    -- (AXI_rvalid is already asserted)
                    -------------------------------------------------------------
                    if AXI_rready = '1' then
                        AXI_rlast <= '0';
                        if words_to_go = 0 then
                            AXI_rvalid  <= '0';
                            state     <= s_idle;
                        elsif words_to_go = 1 then
                            AXI_rlast <= '1';
                        end if;
                        words_to_go <= words_to_go - 1;
                        address_incr <= std_logic_vector(unsigned(address_incr) + 1);                                                 
                    end if;
                when others =>
                    state <= s_idle;                 
            end case;
        end if;
    end process;
end Behavioral;

axi_test_top.vhd

This top level takes the VHDL wrapper generated by the block design, and connects it to my AXI slave. It also connects the registers to the external I/O. My word, it is a whole lot of nothing - damn you structural code.

-------------------------------------------------------------
-- axi_test_top.vhd
--
-- Author: Mike Field <hamster@snap.net.nz>
--
-- Connecting the ARM processor core to  my AXI slave .
-- and the Pynq simple I/O
-------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
library UNISIM;
use UNISIM.VCOMPONENTS.ALL;
entity axi_test_top is
  port (
    DDR_addr          : inout STD_LOGIC_VECTOR ( 14 downto 0 );
    DDR_ba            : inout STD_LOGIC_VECTOR ( 2 downto 0 );
    DDR_cas_n         : inout STD_LOGIC;
    DDR_ck_n          : inout STD_LOGIC;
    DDR_ck_p          : inout STD_LOGIC;
    DDR_cke           : inout STD_LOGIC;
    DDR_cs_n          : inout STD_LOGIC;
    DDR_dm            : inout STD_LOGIC_VECTOR ( 3 downto 0 );
    DDR_dq            : inout STD_LOGIC_VECTOR ( 31 downto 0 );
    DDR_dqs_n         : inout STD_LOGIC_VECTOR ( 3 downto 0 );
    DDR_dqs_p         : inout STD_LOGIC_VECTOR ( 3 downto 0 );
    DDR_odt           : inout STD_LOGIC;
    DDR_ras_n         : inout STD_LOGIC;
    DDR_reset_n       : inout STD_LOGIC;
    DDR_we_n          : inout STD_LOGIC;
    FIXED_IO_ddr_vrn  : inout STD_LOGIC;
    FIXED_IO_ddr_vrp  : inout STD_LOGIC;
    FIXED_IO_mio      : inout STD_LOGIC_VECTOR ( 53 downto 0 );
    FIXED_IO_ps_clk   : inout STD_LOGIC;
    FIXED_IO_ps_porb  : inout STD_LOGIC;
    FIXED_IO_ps_srstb : inout STD_LOGIC;
    LEDs              : out   STD_LOGIC_VECTOR (3 downto 0 );
    rgb0              : out   STD_LOGIC_VECTOR (2 downto 0 );
    rgb1              : out   STD_LOGIC_VECTOR (2 downto 0 );
    buttons           : in    STD_LOGIC_VECTOR (3 downto 0 )
  );
end axi_test_top;

architecture STRUCTURE of axi_test_top is
  component axi_test_wrapper is
  port (
      DDR_cas_n   : inout STD_LOGIC;
      DDR_cke     : inout STD_LOGIC;
      DDR_ck_n    : inout STD_LOGIC;
      DDR_ck_p    : inout STD_LOGIC;
      DDR_cs_n    : inout STD_LOGIC;
      DDR_reset_n : inout STD_LOGIC;
      DDR_odt     : inout STD_LOGIC;
      DDR_ras_n   : inout STD_LOGIC;
      DDR_we_n    : inout STD_LOGIC;
      DDR_ba      : inout STD_LOGIC_VECTOR ( 2 downto 0 );
      DDR_addr    : inout STD_LOGIC_VECTOR ( 14 downto 0 );
      DDR_dm      : inout STD_LOGIC_VECTOR ( 3 downto 0 );
      DDR_dq      : inout STD_LOGIC_VECTOR ( 31 downto 0 );
      DDR_dqs_n   : inout STD_LOGIC_VECTOR ( 3 downto 0 );
      DDR_dqs_p   : inout STD_LOGIC_VECTOR ( 3 downto 0 );
      
      FIXED_IO_mio      : inout STD_LOGIC_VECTOR ( 53 downto 0 );
      FIXED_IO_ddr_vrn  : inout STD_LOGIC;
      FIXED_IO_ddr_vrp  : inout STD_LOGIC;
      FIXED_IO_ps_srstb : inout STD_LOGIC;
      FIXED_IO_ps_clk   : inout STD_LOGIC;
      FIXED_IO_ps_porb  : inout STD_LOGIC;
      
      AXI_ACLK    : in STD_LOGIC;
      AXI_arvalid : out STD_LOGIC;
      AXI_awvalid : out STD_LOGIC;
      AXI_bready  : out STD_LOGIC;
      AXI_rready  : out STD_LOGIC;
      AXI_wlast   : out STD_LOGIC;
      AXI_wvalid  : out STD_LOGIC;
      AXI_arid    : out STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_awid    : out STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_wid     : out STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_arburst : out STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_arlock  : out STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_arsize  : out STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_awburst : out STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_awlock  : out STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_awsize  : out STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_arprot  : out STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_awprot  : out STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_araddr  : out STD_LOGIC_VECTOR ( 31 downto 0 );
      AXI_awaddr  : out STD_LOGIC_VECTOR ( 31 downto 0 );
      AXI_wdata   : out STD_LOGIC_VECTOR ( 31 downto 0 );
      AXI_arcache : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_arlen   : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_arqos   : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_awcache : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_awlen   : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_awqos   : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_wstrb   : out STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_arready : in STD_LOGIC;
      AXI_awready : in STD_LOGIC;
      AXI_bvalid  : in STD_LOGIC;
      AXI_rlast   : in STD_LOGIC;
      AXI_rvalid  : in STD_LOGIC;
      AXI_wready  : in STD_LOGIC;
      AXI_bid     : in STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_rid     : in STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_bresp   : in STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_rresp   : in STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_rdata   : in STD_LOGIC_VECTOR ( 31 downto 0 );
      
      FCLK_CLK0   : out STD_LOGIC
  );
  end component axi_test_wrapper;

  component my_axi_slave is
  port (
      clk         : in  STD_LOGIC;
      AXI_ACLK    : out STD_LOGIC;
      AXI_arvalid : in  STD_LOGIC;
      AXI_awvalid : in  STD_LOGIC;
      AXI_bready  : in  STD_LOGIC;
      AXI_rready  : in  STD_LOGIC;
      AXI_wlast   : in  STD_LOGIC;
      AXI_wvalid  : in  STD_LOGIC;
      AXI_arid    : in  STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_awid    : in  STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_wid     : in  STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_arburst : in  STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_arlock  : in  STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_arsize  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_awburst : in  STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_awlock  : in  STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_awsize  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_arprot  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_awprot  : in  STD_LOGIC_VECTOR ( 2 downto 0 );
      AXI_araddr  : in  STD_LOGIC_VECTOR ( 31 downto 0 );
      AXI_awaddr  : in  STD_LOGIC_VECTOR ( 31 downto 0 );
      AXI_wdata   : in  STD_LOGIC_VECTOR ( 31 downto 0 );
      AXI_arcache : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_arlen   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_arqos   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_awcache : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_awlen   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_awqos   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_wstrb   : in  STD_LOGIC_VECTOR ( 3 downto 0 );
      AXI_arready : out STD_LOGIC;
      AXI_awready : out STD_LOGIC;
      AXI_bvalid  : out STD_LOGIC;
      AXI_rlast   : out STD_LOGIC;
      AXI_rvalid  : out STD_LOGIC;
      AXI_wready  : out STD_LOGIC;
      AXI_bid     : out STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_rid     : out STD_LOGIC_VECTOR ( 11 downto 0 );
      AXI_bresp   : out STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_rresp   : out STD_LOGIC_VECTOR ( 1 downto 0 );
      AXI_rdata   : out STD_LOGIC_VECTOR ( 31 downto 0 );
      
      reg_address      : out std_logic_vector ( 3 downto 0 ) := (others => '0');
      reg_write_data   : out std_logic_vector (31 downto 0 ) := (others => '0');
      reg_write_strobe : out std_logic_vector ( 3 downto 0 ) := (others => '0');
      reg_read_data    : in  std_logic_vector (31 downto 0 ) := (others => '0')  );
  end component my_axi_slave;

    signal AXI_ACLK    : std_logic                      := '0';
    signal AXI_araddr  : std_logic_vector(31 downto 0)  := (others => '0');
    signal AXI_arburst : std_logic_vector(1 downto 0)   := (others => '0');
    signal AXI_arcache : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_arid    : std_logic_vector(11 downto 0)  := (others => '0');
    signal AXI_arlen   : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_arlock  : std_logic_vector(1 downto 0)   := (others => '0');
    signal AXI_arprot  : std_logic_vector(2 downto 0)   := (others => '0');
    signal AXI_arqos   : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_arready : std_logic                      := '0';
    signal AXI_arsize  : std_logic_vector(2 downto 0)   := (others => '0');
    signal AXI_arvalid : std_logic                      := '0';
    signal AXI_awaddr  : std_logic_vector(31 downto 0)  := (others => '0');
    signal AXI_awburst : std_logic_vector(1 downto 0)   := (others => '0');
    signal AXI_awcache : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_awid    : std_logic_vector(11 downto 0)  := (others => '0');
    signal AXI_awlen   : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_awlock  : std_logic_vector(1 downto 0)   := (others => '0');
    signal AXI_awprot  : std_logic_vector(2 downto 0)   := (others => '0');
    signal AXI_awqos   : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_awready : std_logic                      := '0';
    signal AXI_awsize  : std_logic_vector(2 downto 0)   := (others => '0');
    signal AXI_awvalid : std_logic                      := '0';
    signal AXI_bid     : std_logic_vector(11 downto 0)  := (others => '0');
    signal AXI_bready  : std_logic                      := '0';
    signal AXI_bresp   : std_logic_vector(1 downto 0)   := (others => '0');
    signal AXI_bvalid  : std_logic                      := '0';
    signal AXI_rdata   : std_logic_vector(31 downto 0)  := (others => '0');
    signal AXI_rid     : std_logic_vector(11 downto 0)  := (others => '0');
    signal AXI_rlast   : std_logic                      := '0';
    signal AXI_rready  : std_logic                      := '0';
    signal AXI_rresp   : std_logic_vector(1 downto 0)   := (others => '0');
    signal AXI_rvalid  : std_logic                      := '0';
    signal AXI_wdata   : std_logic_vector(31 downto 0)  := (others => '0');
    signal AXI_wid     : std_logic_vector(11 downto 0)  := (others => '0');
    signal AXI_wlast   : std_logic                      := '0';
    signal AXI_wready  : std_logic                      := '0';
    signal AXI_wstrb   : std_logic_vector(3 downto 0)   := (others => '0');
    signal AXI_wvalid  : std_logic                      := '0';

    signal fabric_clk  : std_logic                      := '0';
    
    component reg_file is
        Port ( clk      : in  STD_LOGIC;
               addr     : in  STD_LOGIC_VECTOR (3 downto 0);
               data_in  : in  STD_LOGIC_VECTOR (31 downto 0);
               we       : in  STD_LOGIC_VECTOR (3 downto 0);
               data_out : out STD_LOGIC_VECTOR (31 downto 0);      
               LEDs     : out STD_LOGIC_VECTOR (3 downto 0 );
               rgb0     : out STD_LOGIC_VECTOR (2 downto 0 );
               rgb1     : out STD_LOGIC_VECTOR (2 downto 0 );
               buttons  : in  STD_LOGIC_VECTOR (3 downto 0 )
         );
    end component;
    
    signal reg_addr     : STD_LOGIC_VECTOR ( 3 downto 0) := (others => '0');
    signal reg_data_in  : STD_LOGIC_VECTOR (31 downto 0) := (others => '0');
    signal reg_we       : STD_LOGIC_VECTOR ( 3 downto 0) := (others => '0');
    signal reg_data_out : STD_LOGIC_VECTOR (31 downto 0) := (others => '0');
begin


axi_test_wrapper_i: component axi_test_wrapper
     port map (
      DDR_addr    => DDR_addr,
      DDR_ba      => DDR_ba,
      DDR_cas_n   => DDR_cas_n,
      DDR_ck_n    => DDR_ck_n,
      DDR_ck_p    => DDR_ck_p,
      DDR_cke     => DDR_cke,
      DDR_cs_n    => DDR_cs_n,
      DDR_dm      => DDR_dm(3 downto 0),
      DDR_dq      => DDR_dq(31 downto 0),
      DDR_dqs_n   => DDR_dqs_n(3 downto 0),
      DDR_dqs_p   => DDR_dqs_p,
      DDR_odt     => DDR_odt,
      DDR_ras_n   => DDR_ras_n,
      DDR_reset_n => DDR_reset_n,
      DDR_we_n    => DDR_we_n,
  
      FIXED_IO_ddr_vrn  => FIXED_IO_ddr_vrn,
      FIXED_IO_ddr_vrp  => FIXED_IO_ddr_vrp,
      FIXED_IO_mio      => FIXED_IO_mio,
      FIXED_IO_ps_clk   => FIXED_IO_ps_clk,
      FIXED_IO_ps_porb  => FIXED_IO_ps_porb,
      FIXED_IO_ps_srstb => FIXED_IO_ps_srstb,
      
      AXI_ACLK    => AXI_ACLK,

      -- Bus
      AXI_bid     => AXI_bid,
      AXI_bready  => AXI_bready,
      AXI_bresp   => AXI_bresp,
      AXI_bvalid  => AXI_bvalid,      
      
      -- Read command interface
      AXI_araddr  => AXI_araddr,
      AXI_arburst => AXI_arburst,
      AXI_arcache => AXI_arcache,
      AXI_arid    => AXI_arid,
      AXI_arlen   => AXI_arlen,
      AXI_arlock  => AXI_arlock,
      AXI_arprot  => AXI_arprot,
      AXI_arqos   => AXI_arqos,
      AXI_arready => AXI_arready,
      AXI_arsize  => AXI_arsize,
      AXI_arvalid => AXI_arvalid,

      -- Read data interface
      AXI_rdata   => AXI_rdata,
      AXI_rid     => AXI_rid,
      AXI_rlast   => AXI_rlast,
      AXI_rready  => AXI_rready,
      AXI_rresp   => AXI_rresp,
      AXI_rvalid  => AXI_rvalid,
      
      -- Write command interface 
      AXI_awaddr  => AXI_awaddr,
      AXI_awburst => AXI_awburst,
      AXI_awcache => AXI_awcache,
      AXI_awid    => AXI_awid,
      AXI_awlen   => AXI_awlen,
      AXI_awlock  => AXI_awlock,
      AXI_awprot  => AXI_awprot,
      AXI_awqos   => AXI_awqos,
      AXI_awready => AXI_awready,
      AXI_awsize  => AXI_awsize,
      AXI_awvalid => AXI_awvalid,
      
      -- Write data interface
      AXI_wdata   => AXI_wdata,
      AXI_wid     => AXI_wid,
      AXI_wlast   => AXI_wlast,
      AXI_wready  => AXI_wready,
      AXI_wstrb   => AXI_wstrb,
      AXI_wvalid  => AXI_wvalid,
      
      FCLK_CLK0   => fabric_clk);

i_my_axi_slave: my_axi_slave PORT MAP (
      clk         => fabric_clk,
      AXI_ACLK    => AXI_ACLK,

      -- Bus
      AXI_bid     => AXI_bid,
      AXI_bready  => AXI_bready,
      AXI_bresp   => AXI_bresp,
      AXI_bvalid  => AXI_bvalid,      
      
      -- Read command interface
      AXI_araddr  => AXI_araddr,
      AXI_arburst => AXI_arburst,
      AXI_arcache => AXI_arcache,
      AXI_arid    => AXI_arid,
      AXI_arlen   => AXI_arlen,
      AXI_arlock  => AXI_arlock,
      AXI_arprot  => AXI_arprot,
      AXI_arqos   => AXI_arqos,
      AXI_arready => AXI_arready,
      AXI_arsize  => AXI_arsize,
      AXI_arvalid => AXI_arvalid,

      -- Read data interface
      AXI_rdata   => AXI_rdata,
      AXI_rid     => AXI_rid,
      AXI_rlast   => AXI_rlast,
      AXI_rready  => AXI_rready,
      AXI_rresp   => AXI_rresp,
      AXI_rvalid  => AXI_rvalid,
      
      -- Write command interface 
      AXI_awaddr  => AXI_awaddr,
      AXI_awburst => AXI_awburst,
      AXI_awcache => AXI_awcache,
      AXI_awid    => AXI_awid,
      AXI_awlen   => AXI_awlen,
      AXI_awlock  => AXI_awlock,
      AXI_awprot  => AXI_awprot,
      AXI_awqos   => AXI_awqos,
      AXI_awready => AXI_awready,
      AXI_awsize  => AXI_awsize,
      AXI_awvalid => AXI_awvalid,
      
      -- Write data interface
      AXI_wdata   => AXI_wdata,
      AXI_wid     => AXI_wid,
      AXI_wlast   => AXI_wlast,
      AXI_wready  => AXI_wready,
      AXI_wstrb   => AXI_wstrb,
      AXI_wvalid  => AXI_wvalid,
      ----------------------
      reg_address      => reg_addr,
      reg_write_data   => reg_data_in,
      reg_write_strobe => reg_we,
      reg_read_data    => reg_data_out
    );
      
i_reg_file: reg_file port map (
    clk      => fabric_clk,
    addr     => reg_addr,
    data_in  => reg_data_in,
    we       => reg_we,
    data_out => reg_data_out,
    leds     => LEDs,
    rgb0     => rgb0,
    rgb1     => rgb1,
    buttons  => buttons);

end STRUCTURE;

pynq.xdc

## This file is for the PYNQ-Z1 board Rev. C
set_property -dict { PACKAGE_PIN L15   IOSTANDARD LVCMOS33 } [get_ports { rgb0[2] }]; #IO_L22N_T3_AD7N_35 Sch=led4_b
set_property -dict { PACKAGE_PIN G17   IOSTANDARD LVCMOS33 } [get_ports { rgb0[1] }]; #IO_L16P_T2_35 Sch=led4_g
set_property -dict { PACKAGE_PIN N15   IOSTANDARD LVCMOS33 } [get_ports { rgb0[0] }]; #IO_L21P_T3_DQS_AD14P_35 Sch=led4_r
set_property -dict { PACKAGE_PIN G14   IOSTANDARD LVCMOS33 } [get_ports { rgb1[2] }]; #IO_0_35 Sch=led5_b
set_property -dict { PACKAGE_PIN L14   IOSTANDARD LVCMOS33 } [get_ports { rgb1[1] }]; #IO_L22P_T3_AD7P_35 Sch=led5_g
set_property -dict { PACKAGE_PIN M15   IOSTANDARD LVCMOS33 } [get_ports { rgb1[0] }]; #IO_L23N_T3_35 Sch=led5_r

##LEDs

set_property -dict {PACKAGE_PIN R14 IOSTANDARD LVCMOS33} [get_ports {LEDs[0]}]
set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports {LEDs[1]}]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports {LEDs[2]}]
set_property -dict {PACKAGE_PIN M14 IOSTANDARD LVCMOS33} [get_ports {LEDs[3]}]

##Buttons

set_property -dict { PACKAGE_PIN D19   IOSTANDARD LVCMOS33 } [get_ports { buttons[0] }]; #IO_L4P_T0_35 Sch=btn[0]
set_property -dict { PACKAGE_PIN D20   IOSTANDARD LVCMOS33 } [get_ports { buttons[1] }]; #IO_L4N_T0_35 Sch=btn[1]
set_property -dict { PACKAGE_PIN L20   IOSTANDARD LVCMOS33 } [get_ports { buttons[2] }]; #IO_L9N_T1_DQS_AD3N_35 Sch=btn[2]
set_property -dict { PACKAGE_PIN L19   IOSTANDARD LVCMOS33 } [get_ports { buttons[3] }]; #IO_L9P_T1_DQS_AD3P_35 Sch=btn[3]
 

Hello_world.c

Here's the application that runs on they Zynq processor. It reads the switches and sets the LEDs, and cycles the colours on the RGB LEDs

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"

volatile uint32_t *my_axi = (uint32_t *)(0x43C0<<16);

int main()
{
    int i;

    init_platform();

    print("Hello World\r\n");
    while(1) {
    	unsigned char a = i;
    	// Read the four buttons
    	a = my_axi[1];

    	// Write to the four LEDs
    	my_axi[0] = a;

    	// Change RGB LEDs colour 
        // Bit 3 always reads back as zero!
        if(my_axi[2] & 0x7 == 7)
            my_axi[2] += 9;
        else
        my_axi[2] ++;

    	usleep(100000);
    }
    cleanup_platform();
    return 0;
}
<nowiki>
Personal tools