Microblaze [ PART5 ]

: Presentation
: eCos HAL
: Virtex5 Target board
: Footprint / Dhrystone Benchmark
: Creating external IPs

I. Creating external IPs - Xilinx XPS

The Xilinx Platform Studio (XPS) can also serve to create cutomized IP that require connections to the Microblaze core. IPs can have different complexity, in this example, the IP provides an approximate 100*log10(x) math function. The 100*log10(x) function can be implemented in lookup table. The IP requires two registers of 32 bits. The first register will receive the input value (x). The second register will provide the result of the math function. Depending on the implementation, the 100*log10(x) may require more than 1 clock cycle to complete. The IP can be designed to force the consumer to wait until the result is computed (by delaying the IP2Bus_RdAck signal) or to synchronise the consumer using an interrupt. It is also possible to provide a DONE flag throught a read register to signal when the data is ready.

01. In XPS, select Hardware->Create or Import peripheral...





II. Code structure

The generated IP code is located in the pcores\math_log10_v1_00_a\ directory:

- HDL source file(s)

hdl/vhdl/math_log10.vhd

This is the template file for your peripheral's top design entity. It configures and instantiates the corresponding design units in the way you indicated in the wizard GUI and hooks it up to the stub user logic where the actual functionalites should get implemented. You are not expected to modify this template file except certain marked places for adding user specific generics and ports.

vhdl/user_logic.vhd

This is the template file for the stub user logic design entity, either in VHDL or Verilog, where the actual functionalities should get implemented. Some sample code snippet may be provided for demonstration purpose.

- XPS interface file(s)

data/math_log10_v2_1_0.mpd

This Microprocessor Peripheral Description file contains information of the interface of your peripheral, so that other EDK tools can recognize your peripheral.

data/math_log10_v2_1_0.pao

This Peripheral Analysis Order file defines the analysis order of all the HDL source files that are used to compile your peripheral.

II. VHDL implementation

The generated math_log10.vhd is the top level entry of the IP. This file is left unchanged in this example. However, it shall be updated if customized IOs are required. Update to the entity port or generic should be placed at the location specified by the comments.

entity math_log10 is
  generic
  (
    -- ADD USER GENERICS BELOW THIS LINE ---------------
    --USER generics added here
    -- ADD USER GENERICS ABOVE THIS LINE ---------------

    -- DO NOT EDIT BELOW THIS LINE ---------------------
    -- Bus protocol parameters, do not add to or delete
    C_BASEADDR                  : std_logic_vector     := X"FFFFFFFF";
    C_HIGHADDR                  : std_logic_vector     := X"00000000";
    C_SPLB_AWIDTH               : integer              := 32;
    C_SPLB_DWIDTH               : integer              := 128;
    C_SPLB_NUM_MASTERS          : integer              := 8;
    C_SPLB_MID_WIDTH            : integer              := 3;
    C_SPLB_NATIVE_DWIDTH        : integer              := 32;
    C_SPLB_P2P                  : integer              := 0;
    C_SPLB_SUPPORT_BURSTS       : integer              := 0;
    C_SPLB_SMALLEST_MASTER      : integer              := 32;
    C_SPLB_CLK_PERIOD_PS        : integer              := 10000;
    C_INCLUDE_DPHASE_TIMER      : integer              := 1;
    C_FAMILY                    : string               := "virtex5"
    -- DO NOT EDIT ABOVE THIS LINE ---------------------
  );
  port
  (
    -- ADD USER PORTS BELOW THIS LINE ------------------
    --USER ports added here
    -- ADD USER PORTS ABOVE THIS LINE ------------------

    -- DO NOT EDIT BELOW THIS LINE ---------------------
    -- Bus protocol ports, do not add to or delete
    SPLB_Clk                       : in  std_logic;
    SPLB_Rst                       : in  std_logic;
    PLB_ABus                       : in  std_logic_vector(0 to 31);
    PLB_UABus                      : in  std_logic_vector(0 to 31);
    PLB_PAValid                    : in  std_logic;
    PLB_SAValid                    : in  std_logic;
    PLB_rdPrim                     : in  std_logic;
    PLB_wrPrim                     : in  std_logic;
    PLB_masterID                   : in  std_logic_vector(0 to C_SPLB_MID_WIDTH-1);
    PLB_abort                      : in  std_logic;
    PLB_busLock                    : in  std_logic;
    PLB_RNW                        : in  std_logic;

The generated user_logic.vhd template is the core of the IP. The generated file gives read and write access to the 2 registers of the IP. In this example, the VHDL code is manually updated to only give write access to REG0 (input). The REG1 register is modified to provide the result of the 100*LOG10(REG0) computation. The signal IP2Bus_RdAck is delayed until the computation is completed, forcing the consumer to wait for the data to be ready.

0
------------------------------------------------------------------------------
-- user_logic.vhd - entity/architecture pair
------------------------------------------------------------------------------
--
-- ***************************************************************************
-- ** Copyright (c) 1995-2010 Xilinx, Inc.  All rights reserved.            **
-- **                                                                       **
-- ** Xilinx, Inc.                                                          **
-- ** XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"         **
-- ** AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND       **
-- ** SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,        **
-- ** OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,        **
-- ** APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION           **
-- ** THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,     **
-- ** AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE      **
-- ** FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY              **
-- ** WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE               **
-- ** IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR        **
-- ** REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF       **
-- ** INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS       **
-- ** FOR A PARTICULAR PURPOSE.                                             **
-- **                                                                       **
-- ***************************************************************************
--
------------------------------------------------------------------------------
-- Filename:          user_logic.vhd
-- Version:           1.00.a
-- Description:       User logic.
-- Date:              Sun Mar 11 08:58:07 2012 (by Create and Import Peripheral Wizard)
-- VHDL Standard:     VHDL'93
------------------------------------------------------------------------------
-- Naming Conventions:
--   active low signals:                    "*_n"
--   clock signals:                         "clk", "clk_div#", "clk_#x"
--   reset signals:                         "rst", "rst_n"
--   generics:                              "C_*"
--   user defined types:                    "*_TYPE"
--   state machine next state:              "*_ns"
--   state machine current state:           "*_cs"
--   combinatorial signals:                 "*_com"
--   pipelined or register delay signals:   "*_d#"
--   counter signals:                       "*cnt*"
--   clock enable signals:                  "*_ce"
--   internal version of output port:       "*_i"
--   device pins:                           "*_pin"
--   ports:                                 "- Names begin with Uppercase"
--   processes:                             "*_PROCESS"
--   component instantiations:              "I_<#|FUNC>"
------------------------------------------------------------------------------

-- DO NOT EDIT BELOW THIS LINE --------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
 
library proc_common_v3_00_a;
use proc_common_v3_00_a.proc_common_pkg.all;

-- DO NOT EDIT ABOVE THIS LINE --------------------

--USER libraries added here

------------------------------------------------------------------------------
-- Entity section
------------------------------------------------------------------------------
-- Definition of Generics:
--   C_SLV_DWIDTH                 -- Slave interface data bus width
--   C_NUM_REG                    -- Number of software accessible registers
--
-- Definition of Ports:
--   Bus2IP_Clk                   -- Bus to IP clock
--   Bus2IP_Reset                 -- Bus to IP reset
--   Bus2IP_Data                  -- Bus to IP data bus
--   Bus2IP_BE                    -- Bus to IP byte enables
--   Bus2IP_RdCE                  -- Bus to IP read chip enable
--   Bus2IP_WrCE                  -- Bus to IP write chip enable
--   IP2Bus_Data                  -- IP to Bus data bus
--   IP2Bus_RdAck                 -- IP to Bus read transfer acknowledgement
--   IP2Bus_WrAck                 -- IP to Bus write transfer acknowledgement
--   IP2Bus_Error                 -- IP to Bus error response
------------------------------------------------------------------------------

entity user_logic is
  generic
  (
    -- ADD USER GENERICS BELOW THIS LINE ---------------
    --USER generics added here
    -- ADD USER GENERICS ABOVE THIS LINE ---------------

    -- DO NOT EDIT BELOW THIS LINE ---------------------
    -- Bus protocol parameters, do not add to or delete
    C_SLV_DWIDTH                   : integer              := 32;
    C_NUM_REG                      : integer              := 2
    -- DO NOT EDIT ABOVE THIS LINE ---------------------
  );
  port
  (
    -- ADD USER PORTS BELOW THIS LINE ------------------
    --USER ports added here
    -- ADD USER PORTS ABOVE THIS LINE ------------------

    -- DO NOT EDIT BELOW THIS LINE ---------------------
    -- Bus protocol ports, do not add to or delete
    Bus2IP_Clk                     : in  std_logic;
    Bus2IP_Reset                   : in  std_logic;
    Bus2IP_Data                    : in  std_logic_vector(0 to C_SLV_DWIDTH-1);
    Bus2IP_BE                      : in  std_logic_vector(0 to C_SLV_DWIDTH/8-1);
    Bus2IP_RdCE                    : in  std_logic_vector(0 to C_NUM_REG-1);
    Bus2IP_WrCE                    : in  std_logic_vector(0 to C_NUM_REG-1);
    IP2Bus_Data                    : out std_logic_vector(0 to C_SLV_DWIDTH-1);
    IP2Bus_RdAck                   : out std_logic;
    IP2Bus_WrAck                   : out std_logic;
    IP2Bus_Error                   : out std_logic
    -- DO NOT EDIT ABOVE THIS LINE ---------------------
  );

  attribute SIGIS : string;
  attribute SIGIS of Bus2IP_Clk    : signal is "CLK";
  attribute SIGIS of Bus2IP_Reset  : signal is "RST";

end entity user_logic;

------------------------------------------------------------------------------
-- Architecture section
------------------------------------------------------------------------------

architecture IMP of user_logic is

  --USER signal declarations added here, as needed for user logic

  ------------------------------------------
  -- Signals for user logic slave model s/w accessible register example
  ------------------------------------------
  signal slv_reg0                       : std_logic_vector(0 to C_SLV_DWIDTH-1);
  signal slv_reg1                       : std_logic_vector(0 to C_SLV_DWIDTH-1);
  signal slv_reg_write_sel              : std_logic_vector(0 to 1);
  signal slv_reg_read_sel               : std_logic_vector(0 to 1);
  signal slv_ip2bus_data                : std_logic_vector(0 to C_SLV_DWIDTH-1);
  signal slv_read_ack                   : std_logic;
  signal slv_write_ack                  : std_logic;
  signal fsm_start_log10                : std_logic;
  signal fsm_compl_log10                : std_logic;
  signal fsm_update_reg                 : std_logic;
  signal log10                          : unsigned(0 to 2*C_SLV_DWIDTH-1);
  
begin

  --USER logic implementation added here

  ------------------------------------------
  -- Example code to read/write user logic slave model s/w accessible registers
  -- 
  -- Note:
  -- The example code presented here is to show you one way of reading/writing
  -- software accessible registers implemented in the user logic slave model.
  -- Each bit of the Bus2IP_WrCE/Bus2IP_RdCE signals is configured to correspond
  -- to one software accessible register by the top level template. For example,
  -- if you have four 32 bit software accessible registers in the user logic,
  -- you are basically operating on the following memory mapped registers:
  -- 
  --    Bus2IP_WrCE/Bus2IP_RdCE   Memory Mapped Register
  --                     "1000"   C_BASEADDR + 0x0
  --                     "0100"   C_BASEADDR + 0x4
  --                     "0010"   C_BASEADDR + 0x8

  --                     "0001"   C_BASEADDR + 0xC
  -- 
  ------------------------------------------
  slv_reg_write_sel <= Bus2IP_WrCE(0 to 1);
  slv_reg_read_sel  <= Bus2IP_RdCE(0 to 1);
  slv_write_ack     <= Bus2IP_WrCE(0);-- or Bus2IP_WrCE(1);
  --slv_read_ack      <= Bus2IP_RdCE(0) or Bus2IP_RdCE(1);

  -- implement slave model software accessible register(s)
  SLAVE_REG_WRITE_PROC : process( Bus2IP_Clk ) is
  begin

    if Bus2IP_Clk'event and Bus2IP_Clk = '1' then
      if Bus2IP_Reset = '1' then
        slv_reg0 <= (others => '0');
        slv_reg1 <= (others => '0');
        fsm_start_log10 <= '0';
        fsm_compl_log10 <= '0';
        fsm_update_reg  <= '0';
      else
        case slv_reg_write_sel is
          when "10" =>
            for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
              if ( Bus2IP_BE(byte_index) = '1' ) then
                slv_reg0(byte_index*8 to byte_index*8+7) <= Bus2IP_Data(byte_index*8 to byte_index*8+7);
                fsm_start_log10 <= '1';
                fsm_compl_log10 <= '0';
              end if;
            end loop;
            
--          when "01" =>
--            for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
--              if ( Bus2IP_BE(byte_index) = '1' ) then
--                slv_reg1(byte_index*8 to byte_index*8+7) <= Bus2IP_Data(byte_index*8 to byte_index*8+7);
--              end if;
--            end loop;
          when others => null;
        end case;

        -- Compute LOG10
        if fsm_start_log10 = '1' then
          log10 <= 100_LOG10(unsigned(slv_reg0)); -- 100*LOG10 computation not provided, ready in 1 clock cycle
          fsm_update_reg  <= '1';
          fsm_start_log10 <= '0';
        end if;

        -- Update REG1
        if fsm_update_reg = '1' then
          fsm_update_reg  <= '0';
          fsm_compl_log10 <= '1';
          slv_reg1        <= std_logic_vector(log10);
        end if;
            
        -- Ack register read
        slv_read_ack <= '0';
        if Bus2IP_RdCE(1) = '1' and fsm_compl_log10 = '1' then
          slv_read_ack <= '1';
        end if;        
      end if;
    end if;

  end process SLAVE_REG_WRITE_PROC;

  -- implement slave model software accessible register(s) read mux
  SLAVE_REG_READ_PROC : process( slv_reg_read_sel, slv_reg0, slv_reg1 ) is
  begin

    case slv_reg_read_sel is
      when "10" => slv_ip2bus_data <= slv_reg0;
      when "01" => slv_ip2bus_data <= slv_reg1;
      when others => slv_ip2bus_data <= (others => '0');
    end case;

  end process SLAVE_REG_READ_PROC;


  ------------------------------------------
  -- Example code to drive IP to Bus signals
  ------------------------------------------
  IP2Bus_Data  <= slv_ip2bus_data when slv_read_ack = '1' else
                  (others => '0');

  IP2Bus_WrAck <= slv_write_ack;
  IP2Bus_RdAck <= slv_read_ack;
  IP2Bus_Error <= '0';

end IMP;

Once the VHDL is updated, the IP is inserted in the design. The IP shall be connected to the PLB bus. It is also necessary to memory map the new peripheral ( Addresses -> Generate Addresses ).


Here is a short piece of software to test the newly created peripheral:

   cyg_uint32 res;

   #define XPAR_MATH_LOG10_0_IN  ( XPAR_MATH_LOG10_0_BASEADDR )
   #define XPAR_MATH_LOG10_0_OUT ( XPAR_MATH_LOG10_0_BASEADDR+4 )    

   HAL_WRITE_UINT32(XPAR_MATH_LOG10_0_IN, 1034);
   HAL_READ_UINT32(XPAR_MATH_LOG10_0_OUT, res);

   diag_printf("100*log10(%d) = %d\n", 1034, res);