Microblaze [ PART5 ]
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);