.. _firmware-address-decoder-generation: Autogenerating address decoder firmware from uHAL address tables ================================================================ The VHDL code for address decoder logic can be automatically generated from uHAL address tables using the ``gen_ipbus_addr_decode`` script (part of the uHAL tools package, found under ``/opt/cactus/bin/uhal/tools`` after installation). This script creates the source code for a VHDL package (named ``ipbus_decode_
``) that defines: * integer constants enumerating elements of the bus array after address decoding; and * a function that determines the index of the relevant slave bus from the bus address. This package can be used in combination with the ``ipbus_fabric_sel`` entity in order to create IPbus fabric that multiplexes the IPbus signals for several slaves, based on the bus address. Single-layer example -------------------- Consider the following address table - ``my_module.xml`` - that consists of two registers, two block memories, and a submodule containing three registers. .. code-block:: xml The nodes in the address table that correspond to separate slave buses in the firmware are identified with the ``fwinfo`` attribute. Each 'leaf' node from the node tree must either define the ``fwinfo`` attribute itself, or have an ancestor node that defines this attribute. Where this attribute is defined, its value must be either ``endpoint`` or ``endpoint;width=N``, where ``N`` is the bitwidth of the address range used by slaves connected to that branch of the bus; the width must be specified in this attribute for each 'leaf' endpoint in the bus decode tree. The ``gen_ipbus_addr_decode`` script crawls down the node tree, and creates address decode logic for multiplexing to the first node with a ``fwinfo`` attribute it encounters along all paths down the node tree. For example given the above address table, by running ``gen_ipbus_addr_decode my_module.xml``, the following VHDL will be printed to stdout: .. code-block:: vhdl -- Address decode logic for ipbus fabric -- -- This file has been AUTOGENERATED from the address table - do not hand edit -- -- We assume the synthesis tool is clever enough to recognise exclusive conditions -- in the if statement. -- -- Dave Newbold, February 2011 library IEEE; use IEEE.STD_LOGIC_1164.all; use ieee.numeric_std.all; package ipbus_decode_my_module is constant IPBUS_SEL_WIDTH: positive := 3; subtype ipbus_sel_t is std_logic_vector(IPBUS_SEL_WIDTH - 1 downto 0); function ipbus_sel_my_module(addr : in std_logic_vector(31 downto 0)) return ipbus_sel_t; -- START automatically generated VHDL the Thu Feb 7 22:57:04 2019 constant N_SLV_REG1: integer := 0; constant N_SLV_REG2: integer := 1; constant N_SLV_MEM1: integer := 2; constant N_SLV_MEM2: integer := 3; constant N_SLV_SUBMODULE: integer := 4; constant N_SLAVES: integer := 5; -- END automatically generated VHDL end ipbus_decode_my_module; package body ipbus_decode_my_module is function ipbus_sel_my_module(addr : in std_logic_vector(31 downto 0)) return ipbus_sel_t is variable sel: ipbus_sel_t; begin -- START automatically generated VHDL the Thu Feb 7 22:57:04 2019 if std_match(addr, "----------------0--0-0--------0-") then sel := ipbus_sel_t(to_unsigned(N_SLV_REG1, IPBUS_SEL_WIDTH)); -- reg1 / base 0x00000000 / mask 0x00009402 elsif std_match(addr, "----------------0--0-0--------1-") then sel := ipbus_sel_t(to_unsigned(N_SLV_REG2, IPBUS_SEL_WIDTH)); -- reg2 / base 0x00000002 / mask 0x00009402 elsif std_match(addr, "----------------0--1-0----------") then sel := ipbus_sel_t(to_unsigned(N_SLV_MEM1, IPBUS_SEL_WIDTH)); -- mem1 / base 0x00001000 / mask 0x00009400 elsif std_match(addr, "----------------0--1-1----------") then sel := ipbus_sel_t(to_unsigned(N_SLV_MEM2, IPBUS_SEL_WIDTH)); -- mem2 / base 0x00001400 / mask 0x00009400 elsif std_match(addr, "----------------1--0-0----------") then sel := ipbus_sel_t(to_unsigned(N_SLV_SUBMODULE, IPBUS_SEL_WIDTH)); -- submodule / base 0x00008000 / mask 0x00009400 -- END automatically generated VHDL else sel := ipbus_sel_t(to_unsigned(N_SLAVES, IPBUS_SEL_WIDTH)); end if; return sel; end function ipbus_sel_my_module; end ipbus_decode_my_module; You can see that this VHDL defines a package, ``ipbus_decode_my_module``, containing: * One integer constant - named ``N_SLV_`` - for each node that has a ``fwinfo`` attribute in the above address table * A function, ``ipbus_sel_my_module``, that returns the value of the appropriate constant when given the bus address This automatically-generated package can then be used in combination with the ``ipbus_fabric_sel`` entity in order to multiplex IPbus signals to slaves in VHDL as shown below: .. code-block:: vhdl library IEEE; use IEEE.STD_LOGIC_1164.ALL; use work.ipbus.all; use work.ipbus_decode_my_module.all; entity my_module is port( ipb_clk: in std_logic; ipb_rst: in std_logic; ipb_in: in ipb_wbus; ipb_out: out ipb_rbus ); end my_module; architecture rtl of my_module is signal ipbw: ipb_wbus_array(N_SLAVES - 1 downto 0); signal ipbr: ipb_rbus_array(N_SLAVES - 1 downto 0); begin -- ipbus address decode fabric: entity work.ipbus_fabric_sel generic map( NSLV => N_SLAVES, SEL_WIDTH => IPBUS_SEL_WIDTH) port map( ipb_in => ipb_in, ipb_out => ipb_out, sel => ipbus_decode_my_module(ipb_in.ipb_addr), ipb_to_slaves => ipbw, ipb_from_slaves => ipbr ); -- Registers reg1: entity work.ipbus_reg_v port map( clk => ipb_clk, reset => ipb_rst, ipbus_in => ipbw(N_SLV_REG1), ipbus_out => ipbr(N_SLV_REG1), q => open ); reg2: entity work.ipbus_reg_v port map( clk => ipb_clk, reset => ipb_rst, ipbus_in => ipbw(N_SLV_REG2), ipbus_out => ipbr(N_SLV_REG2), q => open ); -- Block memories mem1: entity work.ipbus_dpram generic map(ADDR_WIDTH => 10) port map( clk => ipb_clk, reset => ipb_rst, ipbus_in => ipbw(N_SLV_MEM1), ipbus_out => ipbr(N_SLV_MEM1), rclk => ipb_clk, q => open, addr => (Others => '0') ); mem2: entity work.ipbus_dpram generic map(ADDR_WIDTH => 10) port map( clk => ipb_clk, reset => ipb_rst, ipbus_in => ipbw(N_SLV_MEM2), ipbus_out => ipbr(N_SLV_MEM2), rclk => ipb_clk, q => open, addr => (Others => '0') ); -- submodule submod: entity work.my_submodule port map( clk => ipb_clk, reset => ipb_rst, ipbus_in => ipbw(N_SLV_SUBMODULE), ipbus_out => ipbr(N_SLV_SUBMODULE) ); end rtl;