GCC Code Coverage Report
Directory: generated/vunit_out/preprocessed/ Exec Total Coverage
File: generated/vunit_out/preprocessed/axi/axi_write_throttle.vhd Lines: 47 47 100.0 %
Date: 2021-06-12 04:12:08 Branches: 197 246 80.1 %

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.w side.
12
-- Using the level from that FIFO, the throttling will make sure that an address
13
-- transactio is not done until all data for that transaction is available in
14
-- the FIFO. This avoids stalling on the throttled_m2s.w 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











19404
entity axi_write_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 AW channel is pipelined one step to improve poor timing, mainly on AWVALID.
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_aw_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_write_m2s_t := axi_write_m2s_init;
53
    input_s2m : out axi_write_s2m_t := axi_write_s2m_init;
54
    --
55
    throttled_m2s : out axi_write_m2s_t := axi_write_m2s_init;
56
    throttled_s2m : in axi_write_s2m_t := axi_write_s2m_init
57
  );
58
end entity;
59
60
architecture a of axi_write_throttle is
61
62
612
  signal pipelined_m2s_aw : axi_m2s_a_t := axi_m2s_a_init;
63
6
  signal pipelined_s2m_aw : 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 AWLEN 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
  -- Since W transactions can happen before AW transaction,
76
  -- the counters can become negative as well as positive.
77
  subtype data_counter_t is integer range -data_fifo_depth to data_fifo_depth;
78
79
  -- Negation of:
80
  -- Data beats that are available in the FIFO, but have not yet been claimed by
81
  -- an address transaction.
82
6
  signal minus_num_beats_available_but_not_negotiated : data_counter_t := 0;
83
84
  -- Number of data beats that have been negotiated through an address transaction,
85
  -- but have not yet been sent via data transactions. Aka outstanding beats.
86
6
  signal num_beats_negotiated_but_not_sent : data_counter_t := 0;
87
88
begin
89
90
  ------------------------------------------------------------------------------
91
  pipeline : block
92
6
    constant m2s_length : positive := axi_m2s_a_sz(id_width=>id_width, addr_width=>addr_width);
93
1014
    signal input_m2s_aw_slv, pipelined_m2s_aw_slv :
94
      std_logic_vector(m2s_length - 1 downto 0) := (others => '0');
95
12
    signal pipelined_valid : std_logic := '0';
96
  begin
97
98
517644
    input_m2s_aw_slv <= to_slv(data=>input_m2s.aw, id_width=>id_width, addr_width=>addr_width);
99
100
101
    ------------------------------------------------------------------------------
102
6
    handshake_pipeline_inst : entity common.handshake_pipeline
103
      generic map (
104
        data_width => input_m2s_aw_slv'length,
105
        -- Choosable by user, since it affects footprint.
106
        full_throughput => full_aw_throughput,
107
        -- The goal of this pipeline is to improve timing of the control bits, so this one must
108
        -- be false, even though it will increase footprint.
109
        allow_poor_input_ready_timing => false
110
      )
111
      port map (
112
        clk => clk,
113
        --
114
        input_ready => input_s2m.aw.ready,
115
        input_valid => input_m2s.aw.valid,
116
        input_data => input_m2s_aw_slv,
117
        --
118
        output_ready => pipelined_s2m_aw.ready,
119
        output_valid => pipelined_valid,
120
        output_data => pipelined_m2s_aw_slv
121
      );
122
123
124
    ------------------------------------------------------------------------------
125
864
    assign_aw : process(all)
126
    begin
127








1225836
      pipelined_m2s_aw <= to_axi_m2s_a(
128
        data=>pipelined_m2s_aw_slv,
129
        id_width=>id_width,
130
        addr_width=>addr_width
131
      );
132

12018
      pipelined_m2s_aw.valid <= pipelined_valid;
133
    end process;
134
135
  end block;
136
137
138
  -- Two complement inversion: inv(len) = - len - 1 = - (len + 1) = - burst_length_beats
139





208
  minus_burst_length_beats <= not signed('0' & pipelined_m2s_aw.len(len_range));
140
141
  ------------------------------------------------------------------------------
142
6
  assign_throttled_bus : process(all)
143
3584
    variable block_address_transactions : boolean;
144
  begin
145








11425020
    throttled_m2s.aw <= pipelined_m2s_aw;
146

112010
    pipelined_s2m_aw <= throttled_s2m.aw;
147
148






18929690
    throttled_m2s.w <= input_m2s.w;
149

112010
    input_s2m.w <= throttled_s2m.w;
150
151

112010
    throttled_m2s.b <= input_m2s.b;
152




3024270
    input_s2m.b <= throttled_s2m.b;
153
154
    -- The original condition would have been
155
    --
156
    -- block_address_transactions =
157
    --   burst_length_beats > num_beats_available_but_not_negotiated
158
    --
159
    -- where num_beats_available_but_not_negotiated =
160
    --   data_fifo_level - num_beats_negotiated_but_not_sent
161
    --
162
    -- where num_beats_negotiated_but_not_sent was given by accumulating
163
    --   to_int(address_transaction) * burst_length_beats - to_int(data_transaction)
164
    --
165
    -- However this created a very long critical path from AWLEN to AWVALID. The
166
    -- bytes_per_beat = AWLEN + 1 term, used in two places was replaced with -inv(AWLEN).
167
    -- The minus sign was moved to the right side of the expression, which changed the subtraction
168
    -- order. This makes the signals and their ranges a bit harder to understand, but it improves
169
    -- the critical path a lot.
170
112010
    block_address_transactions :=
171
      minus_burst_length_beats < minus_num_beats_available_but_not_negotiated;
172
112010
    if block_address_transactions then
173

96087
      throttled_m2s.aw.valid <= '0';
174


842029
      pipelined_s2m_aw.ready <= '0';
175
    end if;
176
  end process;
177
178
179
  ------------------------------------------------------------------------------
180
6
  count : process
181
6
    variable num_beats_negotiated_but_not_sent_int : data_counter_t := 0;
182
44
    variable aw_term : signed(minus_burst_length_beats'range) := (others => '0');
183
  begin
184

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

186485
    minus_num_beats_available_but_not_negotiated <=
200
      num_beats_negotiated_but_not_sent_int - data_fifo_level;
201
202

372970
    num_beats_negotiated_but_not_sent <= num_beats_negotiated_but_not_sent_int;
203
  end process;
204
205

82959
  address_transaction <= throttled_s2m.aw.ready and throttled_m2s.aw.valid;
206

12018
  data_transaction <= throttled_s2m.w.ready and throttled_m2s.w.valid;
207
208
end architecture;