Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion hdl/ip/vhd/axi_blocks/BUCK
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("//tools:hdl.bzl", "vhdl_unit")
load("//tools:hdl.bzl", "vhdl_unit", "vunit_sim")

vhdl_unit(
name = "axilite_common_pkg",
Expand Down Expand Up @@ -86,11 +86,32 @@ vhdl_unit(
visibility = ['PUBLIC'],
)

vhdl_unit(
name = "axil_pipe",
srcs = glob(["axil_pipe.vhd"]),
deps = [
":axilite_common_pkg",
],
standard = "2008",
visibility = ['PUBLIC'],
)

vhdl_unit(
name = "axil_interconnect_2k8",
srcs = glob(["axil_interconnect_2k8.vhd"]),
deps = [
":axilite_if_2k8",
":axil_pipe",
],
standard = "2008",
visibility = ['PUBLIC'],
)

vunit_sim(
name = "axil_interconnect_tb",
srcs = glob(["sims/*.vhd"]),
deps = [
":axil_interconnect_2k8",
],
standard = "2008",
visibility = ['PUBLIC'],
Expand Down
12 changes: 7 additions & 5 deletions hdl/ip/vhd/axi_blocks/axil8_resizer.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ begin
responder.write_data.strb <= fabric.write_data.strb;

responder.write_response.ready <= fabric.write_response.ready;
fabric.read_address.valid <= responder.read_address.valid;
responder.read_address.addr <= fabric.read_address.addr(responder.read_address.addr'length - 1 downto 0);
fabric.read_data.ready <= responder.read_data.ready;
fabric.write_response.resp <= responder.write_response.resp;
fabric.write_response.valid <= responder.write_response.valid;

responder.read_address.valid <= fabric.read_address.valid;
responder.read_address.addr <= fabric.read_address.addr(responder.read_address.addr'length - 1 downto 0);
fabric.read_address.ready <= responder.read_address.ready;
responder.read_data.resp <= fabric.read_data.resp;
responder.read_data.valid <= fabric.read_data.valid;

responder.read_data.ready <= fabric.read_data.ready;
fabric.read_data.resp <= responder.read_data.resp;
fabric.read_data.valid <= responder.read_data.valid;
fabric.read_data.data <= responder.read_data.data;
end rtl;
119 changes: 119 additions & 0 deletions hdl/ip/vhd/axi_blocks/axil_common_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,133 @@ package axil_common_pkg is
type axil_responder_config is record
base_addr : std_logic_vector(31 downto 0);
addr_span_bits : integer;
-- Register stages the interconnect inserts in *each* direction between
-- the fabric and this responder, to buy back setup time on a responder
-- that is physically far from the fabric. Added round trip latency is
-- 2 * pipe_stages cycles plus a few fixed handshake cycles, so raise it
-- only where timing needs it. 0 is a zero cost pass-through.
pipe_stages : natural;
end record;

type axil_responder_cfg_array_t is array (natural range <>) of axil_responder_config;

-- Constructor for a responder config, so adding a field later does not
-- break every aggregate in the tree.
function resp_cfg (
constant base_addr : std_logic_vector(31 downto 0);
constant addr_span_bits : integer;
constant pipe_stages : natural := 0
) return axil_responder_config;

type int_array is array (natural range <>) of integer;

constant OKAY : std_logic_vector(1 downto 0) := "00";
constant EXOKAY : std_logic_vector(1 downto 0) := "01";
constant SLVERR : std_logic_vector(1 downto 0) := "10";

-- Ones in the low addr_span_bits positions, zeros above. Used both to mask
-- a fabric address down to what a responder actually decodes and to select
-- the bits that participate in the address compare.
function span_mask (constant addr_span_bits : integer) return std_logic_vector;

-- Every responder base address must be aligned to its own span, otherwise
-- the upper-bits equality compare in the interconnect is not equivalent to
-- a base/limit compare.
function bases_aligned (constant cfg : axil_responder_cfg_array_t) return boolean;

-- No two responder address ranges may overlap, otherwise more than one bit
-- of the interconnect's one-hot select can be set at once.
function ranges_disjoint (constant cfg : axil_responder_cfg_array_t) return boolean;

-- Every responder must be reachable from an initiator of the given address
-- width, otherwise it silently falls through to the error responder.
function bases_reachable (
constant cfg : axil_responder_cfg_array_t;
constant initiator_addr_width : integer
) return boolean;

end package;

package body axil_common_pkg is

function resp_cfg (
constant base_addr : std_logic_vector(31 downto 0);
constant addr_span_bits : integer;
constant pipe_stages : natural := 0
) return axil_responder_config is
begin
return (base_addr => base_addr,
addr_span_bits => addr_span_bits,
pipe_stages => pipe_stages);
end function;

function span_mask (constant addr_span_bits : integer) return std_logic_vector is
variable mask : std_logic_vector(31 downto 0) := (others => '0');
begin
-- Set every bit below addr_span_bits. Walking the bits one at a time
-- rather than slicing keeps addr_span_bits out of a slice bound, so this
-- stays legal for the 0 and 32 cases (which would be null slices) and
-- stays synthesizable under both Vivado and ghdl.
for i in mask'reverse_range loop
if i < addr_span_bits then
mask(i) := '1';
end if;
end loop;
return mask;
end function;

function bases_aligned (constant cfg : axil_responder_cfg_array_t) return boolean is
begin
-- Visit every responder and bail out on the first misaligned base. A base
-- is aligned when none of the bits inside its own span are set, which is
-- exactly the bits span_mask selects.
for i in cfg'range loop
if (cfg(i).base_addr and span_mask(cfg(i).addr_span_bits)) /= 32x"0" then
return false;
end if;
end loop;
return true;
end function;

function ranges_disjoint (constant cfg : axil_responder_cfg_array_t) return boolean is
variable wider : integer;
begin
-- Visit each unordered pair of responders once (the j > i guard is what
-- skips the self comparison and the mirror of a pair already checked) and
-- bail out on the first overlap.
for i in cfg'range loop
for j in cfg'range loop
if j > i then
-- both ranges are power of two aligned, so they overlap if
-- and only if the bases agree above the wider of the spans
wider := cfg(i).addr_span_bits;
if cfg(j).addr_span_bits > wider then
wider := cfg(j).addr_span_bits;
end if;
if ((cfg(i).base_addr xor cfg(j).base_addr) and not span_mask(wider)) = 32x"0" then
return false;
end if;
end if;
end loop;
end loop;
return true;
end function;

function bases_reachable (
constant cfg : axil_responder_cfg_array_t;
constant initiator_addr_width : integer
) return boolean is
begin
-- Visit every responder and bail out on the first base the initiator
-- cannot drive, which is any base with a bit set at or above the
-- initiator's address width. Reusing span_mask here treats the initiator
-- width as a span: everything outside it must be zero.
for i in cfg'range loop
if (cfg(i).base_addr and not span_mask(initiator_addr_width)) /= 32x"0" then
return false;
end if;
end loop;
return true;
end function;

end package body;
Loading
Loading