GCC Code Coverage Report
Directory: generated/vunit_out/preprocessed/ Exec Total Coverage
File: generated/vunit_out/preprocessed/axi/axi_read_throttle.vhd Lines: 46 46 100.0 %
Date: 2021-06-12 04:12:08 Branches: 173 220 78.6 %

Line Branch Exec Source
1
36
-- -------------------------------------------------------------------------------------------------
2
-- Copyright (c) Lukas Vik. All rights reserved.
3
--
4
-- This file is part of the tsfpga project.
5
-- https://tsfpga.com
6
-- https://gitlab.com/tsfpga/tsfpga
7
-- -------------------------------------------------------------------------------------------------
8
-- Performs throttling of an AXI bus by limiting the number of outstanding
9
-- transactions.
10
--
11
-- This entity is to be used in conjuction with a data FIFO on the input.r side.
12
-- Using the level from that FIFO, the throttling will make sure that address
13
-- transactions are not made that would result in the FIFO becoming full. This
14
-- avoids stalling on the throttled_s2m.r channel.
15
--
16
-- To achieve this it keeps track of the number of outstanding beats
17
-- that have been negotiated but not yet sent.
18
-- -------------------------------------------------------------------------------------------------
19
20
library ieee;
21
use ieee.numeric_std.all;
22
use ieee.std_logic_1164.all;
23
24
library axi;
25
use axi.axi_pkg.all;
26
27
library common;
28
use common.types_pkg.all;
29
30
library math;
31
6
use math.math_pkg.all;
32
33
34









18444
entity axi_read_throttle is
35
  generic(
36
6
    data_fifo_depth : positive;
37
6
    max_burst_length_beats : positive;
38
6
    id_width : natural;
39


762
    addr_width : positive;
40
    -- The AR channel is pipelined one step to improve poor timing, mainly on ARVALID.
41
    -- If this generic is set to false, the pipelining will be of a simpler model that has lower
42
    -- logic footprint, but only allow a transaction every third clock cycle. If it is set to true,
43
    -- the pipeline will support a transaction every clock cycle, at the cost of a greater
44
    -- logic footprint.
45
    full_ar_throughput : boolean
46
  );
47
  port(
48
    clk : in std_logic;
49
    --
50
    data_fifo_level : in integer range 0 to data_fifo_depth;
51
    --
52
    input_m2s : in axi_read_m2s_t := axi_read_m2s_init;
53
    input_s2m : out axi_read_s2m_t := axi_read_s2m_init;
54
    --
55
    throttled_m2s : out axi_read_m2s_t := axi_read_m2s_init;
56
    throttled_s2m : in axi_read_s2m_t := axi_read_s2m_init
57
  );
58
end entity;
59
60
architecture a of axi_read_throttle is
61
62
612
  signal pipelined_m2s_ar : axi_m2s_a_t := axi_m2s_a_init;
63
6
  signal pipelined_s2m_ar : axi_s2m_a_t := axi_s2m_a_init;
64
65
6
  signal address_transaction, data_transaction : std_logic := '0';
66
67
  -- The bits of the ARLEN field that shall be taken into account
68
6
  constant len_width : positive := num_bits_needed(max_burst_length_beats - 1);
69
  subtype len_range is integer range len_width - 1 downto 0;
70
71
    -- +1 in range for sign bit
72
82
  signal minus_burst_length_beats : signed(len_width + 1 - 1 downto 0) :=
73
    (others => '0');
74
75
  -- Number of data beats that have been negotiated via address transactions,
76
  -- but have not yet been sent by the master. Aka outstanding beats.
77
  subtype data_counter_t is integer range 0 to data_fifo_depth;
78
6
  signal num_beats_negotiated_but_not_sent : data_counter_t := 0;
79
80
  -- Negation of:
81
  -- Number of data beat words empty in the FIFO, that are not claimed by oustanding transactions.
82
6
  signal minus_num_empty_words_in_fifo_that_have_not_been_negotiated :
83
    integer range -data_fifo_depth to 0 := 0;
84
85
begin
86
87
  ------------------------------------------------------------------------------
88
  pipeline : block
89
6
    constant m2s_length : positive := axi_m2s_a_sz(id_width=>id_width, addr_width=>addr_width);
90
1014
    signal input_m2s_ar_slv, pipelined_m2s_ar_slv :
91
      std_logic_vector(m2s_length - 1 downto 0) := (others => '0');
92
12
    signal pipelined_valid : std_logic := '0';
93
  begin
94
95
517644
    input_m2s_ar_slv <= to_slv(data=>input_m2s.ar, id_width=>id_width, addr_width=>addr_width);
96
97
98
    ------------------------------------------------------------------------------
99
6
    handshake_pipeline_inst : entity common.handshake_pipeline
100
      generic map (
101
        data_width => input_m2s_ar_slv'length,
102
        -- Choosable by user, since it affects footprint.
103
        full_throughput => full_ar_throughput,
104
        -- The goal of this pipeline is to improve timing of the control bits, so this one must
105
        -- be false, even though it will increase footprint.
106
        allow_poor_input_ready_timing => false
107
      )
108
      port map (
109
        clk => clk,
110
        --
111
        input_ready => input_s2m.ar.ready,
112
        input_valid => input_m2s.ar.valid,
113
        input_data => input_m2s_ar_slv,
114
        --
115
        output_ready => pipelined_s2m_ar.ready,
116
        output_valid => pipelined_valid,
117
        output_data => pipelined_m2s_ar_slv
118
      );
119
120
121
    ------------------------------------------------------------------------------
122
864
    assign_ar : process(all)
123
    begin
124








1225836
      pipelined_m2s_ar <= to_axi_m2s_a(
125
        data=>pipelined_m2s_ar_slv,
126
        id_width=>id_width,
127
        addr_width=>addr_width
128
      );
129

12018
      pipelined_m2s_ar.valid <= pipelined_valid;
130
    end process;
131
132
  end block;
133
134
135
  -- Two complement inversion: inv(len) = - len - 1 = - (len + 1) = - burst_length_beats
136





208
  minus_burst_length_beats <= not signed('0' & pipelined_m2s_ar.len(len_range));
137
138
139
  ------------------------------------------------------------------------------
140
6
  assign : process(all)
141
3104
    variable block_address_transactions : boolean;
142
  begin
143








10004874
    throttled_m2s.ar <= pipelined_m2s_ar;
144

98087
    pipelined_s2m_ar <= throttled_s2m.ar;
145
146

98087
    throttled_m2s.r <= input_m2s.r;
147






15203485
    input_s2m.r <= throttled_s2m.r;
148
149
    -- The original condition would have been
150
    --
151
    -- block_address_transactions =
152
    --   burst_length_beats >= num_empty_words_in_fifo_that_have_not_been_negotiated
153
    --
154
    -- where num_empty_words_in_fifo_that_have_not_been_negotiated =
155
    --   num_empty_words_in_fifo - num_beats_negotiated_but_not_sent
156
    --
157
    -- where num_beats_negotiated_but_not_sent was given by accumulating
158
    --   to_int(address_transaction) * burst_length_beats - to_int(data_transaction)
159
    --
160
    -- However this created a very long critical path from ARLEN to ARVALID. The
161
    -- bytes_per_beat = ARLEN + 1 term, used in two places was replaced with -inv(ARLEN).
162
    -- The minus sign was moved to the right side of the expression, which changed the subtraction
163
    -- order. This makes the signals and their ranges a bit harder to understand, but it improves
164
    -- the critical path a lot.
165
98087
    block_address_transactions :=
166
      minus_burst_length_beats <= minus_num_empty_words_in_fifo_that_have_not_been_negotiated;
167
98087
    if block_address_transactions then
168

18
      throttled_m2s.ar.valid <= '0';
169


745960
      pipelined_s2m_ar.ready <= '0';
170
    end if;
171
  end process;
172
173
174
  ------------------------------------------------------------------------------
175
6
  count : process
176
6
    variable num_empty_words_in_fifo, num_beats_negotiated_but_not_sent_int
177
      : data_counter_t := 0;
178
44
    variable ar_term : signed(minus_burst_length_beats'range) := (others => '0');
179
  begin
180

745942
    wait until rising_edge(clk);
181
182
    -- This muxing results in a shorter critical path than doing
183
    -- e.g. minus_burst_length_beats * to_int(address_transaction).
184
    -- LUT usage stayed the same.
185
186485
    if address_transaction then
186
3000
      ar_term := minus_burst_length_beats;
187
    else
188
1468954
      ar_term := (others => '0');
189
    end if;
190
191
186485
    num_beats_negotiated_but_not_sent_int := num_beats_negotiated_but_not_sent
192
      - to_integer(ar_term)
193
      - to_int(data_transaction);
194
195
186485
    num_empty_words_in_fifo := data_fifo_depth - data_fifo_level;
196

186485
    minus_num_empty_words_in_fifo_that_have_not_been_negotiated <=
197
      num_beats_negotiated_but_not_sent_int - num_empty_words_in_fifo;
198
199

372970
    num_beats_negotiated_but_not_sent <= num_beats_negotiated_but_not_sent_int;
200
  end process;
201
202

82916
  address_transaction <= throttled_m2s.ar.valid and throttled_s2m.ar.ready;
203

6024
  data_transaction <= throttled_m2s.r.ready and throttled_s2m.r.valid;
204
205
end architecture;