blob: 7285c6e0b428899177c220c188968c0def54a3cf [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Life cycle gating module for TL-UL protocol.
// Transactions are passed through when lc_en_i == ON.
// In all other cases (lc_en_i != ON) incoming transactions return a bus error.
//
// Note that the lc_en_i should be synchronized and buffered outside of this module using
// an instance of prim_lc_sync.
module tlul_lc_gate
import tlul_pkg::*;
import lc_ctrl_pkg::*;
#(
// Number of LC gating muxes in each direction.
// It is recommended to set this parameter to 2, which results
// in a total of 4 gating muxes.
parameter int NumGatesPerDirection = 2
) (
input clk_i,
input rst_ni,
// To host
input tl_h2d_t tl_h2d_i,
output tl_d2h_t tl_d2h_o,
// To device
output tl_h2d_t tl_h2d_o,
input tl_d2h_t tl_d2h_i,
// LC control signal
input lc_tx_t lc_en_i
);
//////////////////
// Access Gates //
//////////////////
// Make a separate MUBI copy for each gating mux.
lc_ctrl_pkg::lc_tx_t [2*NumGatesPerDirection:0] lc_en_buf;
prim_lc_sync #(
.NumCopies(2*NumGatesPerDirection+1),
.AsyncOn(0)
) u_prim_lc_sync (
.clk_i,
.rst_ni,
.lc_en_i(lc_en_i),
.lc_en_o(lc_en_buf)
);
tl_h2d_t tl_h2d_int [NumGatesPerDirection+1];
tl_d2h_t tl_d2h_int [NumGatesPerDirection+1];
for (genvar k = 0; k < NumGatesPerDirection; k++) begin : gen_lc_gating_muxes
// H -> D path.
prim_blanker #(
.Width($bits(tl_h2d_t))
) u_prim_blanker_h2d (
.in_i(tl_h2d_int[k]),
.en_i(lc_tx_test_true_strict(lc_en_buf[2*k])),
.out_o(tl_h2d_int[k+1])
);
// D -> H path.
prim_blanker #(
.Width($bits(tl_d2h_t))
) u_prim_blanker_d2h (
.in_i(tl_d2h_int[k+1]),
.en_i(lc_tx_test_true_strict(lc_en_buf[2*k+1])),
.out_o(tl_d2h_int[k])
);
end
// Assign signals on the device side.
assign tl_h2d_o = tl_h2d_int[NumGatesPerDirection];
assign tl_d2h_int[NumGatesPerDirection] = tl_d2h_i;
///////////////////////////
// Host Side Interposing //
///////////////////////////
// At the host side, we interpose the ready / valid signals so that we can return a bus error
// in case the lc signal is not set to ON. Note that this logic does not have to be duplicated
// since erroring back is considered a convenience feature so that the bus does not lock up.
tl_h2d_t tl_h2d_error;
tl_d2h_t tl_d2h_error;
always_comb begin
tl_h2d_int[0] = '0;
tl_d2h_o = '0;
tl_h2d_error = '0;
if (lc_tx_test_true_strict(lc_en_buf[2*NumGatesPerDirection]) && !tl_d2h_error.d_valid) begin
tl_h2d_int[0] = tl_h2d_i;
tl_d2h_o = tl_d2h_int[0];
end else begin
// In this case we route the incoming transactions through the error responder submodule.
tl_h2d_error = tl_h2d_i;
tl_d2h_o = tl_d2h_error;
end
end
tlul_err_resp u_tlul_err_resp (
.clk_i,
.rst_ni,
.tl_h_i(tl_h2d_error),
.tl_h_o(tl_d2h_error)
);
endmodule : tlul_lc_gate