| // 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, |
| |
| // Flush control signaling |
| input flush_req_i, |
| output logic flush_ack_o, |
| |
| // Indicates whether there are pending responses on the device side. |
| output logic resp_pending_o, |
| |
| // LC control signal |
| input lc_tx_t lc_en_i, |
| output logic err_o |
| ); |
| |
| ////////////////// |
| // Access Gates // |
| ////////////////// |
| |
| lc_tx_t err_en; |
| lc_tx_t [NumGatesPerDirection-1:0] err_en_buf; |
| |
| prim_lc_sync #( |
| .NumCopies(NumGatesPerDirection), |
| .AsyncOn(0) |
| ) u_err_en_sync ( |
| .clk_i, |
| .rst_ni, |
| .lc_en_i(err_en), |
| .lc_en_o(err_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_false_strict(err_en_buf[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_false_strict(err_en_buf[k])), |
| .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 // |
| /////////////////////////// |
| |
| // Encoding generated with: |
| // $ ./util/design/sparse-fsm-encode.py -d 5 -m 4 -n 8 \ |
| // -s 3379253306 --language=sv |
| // |
| // Hamming distance histogram: |
| // |
| // 0: -- |
| // 1: -- |
| // 2: -- |
| // 3: -- |
| // 4: -- |
| // 5: |||||||||||||||||||| (66.67%) |
| // 6: |||||||||| (33.33%) |
| // 7: -- |
| // 8: -- |
| // |
| // Minimum Hamming distance: 5 |
| // Maximum Hamming distance: 6 |
| // Minimum Hamming weight: 3 |
| // Maximum Hamming weight: 5 |
| // |
| // Encoding generated with: |
| // $ ./util/design/sparse-fsm-encode.py -d 5 -m 5 -n 9 \ |
| // -s 686407169 --language=sv |
| // |
| // Hamming distance histogram: |
| // |
| // 0: -- |
| // 1: -- |
| // 2: -- |
| // 3: -- |
| // 4: -- |
| // 5: |||||||||||||||||||| (60.00%) |
| // 6: ||||||||||||| (40.00%) |
| // 7: -- |
| // 8: -- |
| // 9: -- |
| // |
| // Minimum Hamming distance: 5 |
| // Maximum Hamming distance: 6 |
| // Minimum Hamming weight: 3 |
| // Maximum Hamming weight: 6 |
| // |
| localparam int StateWidth = 9; |
| typedef enum logic [StateWidth-1:0] { |
| StActive = 9'b100100001, |
| StOutstanding = 9'b011100111, |
| StFlush = 9'b001001100, |
| StError = 9'b010111010, |
| StErrorOutstanding = 9'b100010110 |
| } state_e; |
| |
| state_e state_d, state_q; |
| `PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, state_e, StError) |
| |
| logic [1:0] outstanding_txn; |
| logic a_ack; |
| logic d_ack; |
| assign a_ack = tl_h2d_i.a_valid & tl_d2h_o.a_ready; |
| assign d_ack = tl_h2d_i.d_ready & tl_d2h_o.d_valid; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| outstanding_txn <= '0; |
| end else if (a_ack && !d_ack) begin |
| outstanding_txn <= outstanding_txn + 1'b1; |
| end else if (d_ack && !a_ack) begin |
| outstanding_txn <= outstanding_txn - 1'b1; |
| end |
| end |
| |
| logic block_cmd; |
| always_comb begin |
| block_cmd = '0; |
| state_d = state_q; |
| err_en = Off; |
| err_o = '0; |
| flush_ack_o = '0; |
| resp_pending_o = 1'b0; |
| |
| unique case (state_q) |
| StActive: begin |
| if (lc_tx_test_false_loose(lc_en_i) || flush_req_i) begin |
| state_d = StOutstanding; |
| end |
| if (outstanding_txn != '0) begin |
| resp_pending_o = 1'b1; |
| end |
| end |
| |
| StOutstanding: begin |
| block_cmd = 1'b1; |
| if (outstanding_txn == '0) begin |
| state_d = lc_tx_test_false_loose(lc_en_i) ? StError : StFlush; |
| end else begin |
| resp_pending_o = 1'b1; |
| end |
| end |
| |
| StFlush: begin |
| block_cmd = 1'b1; |
| flush_ack_o = 1'b1; |
| if (lc_tx_test_false_loose(lc_en_i)) begin |
| state_d = StError; |
| end else if (!flush_req_i) begin |
| state_d = StActive; |
| end |
| end |
| |
| StError: begin |
| err_en = On; |
| if (lc_tx_test_true_strict(lc_en_i)) begin |
| state_d = StErrorOutstanding; |
| end |
| end |
| |
| StErrorOutstanding: begin |
| err_en = On; |
| block_cmd = 1'b1; |
| if (outstanding_txn == '0) begin |
| state_d = StActive; |
| end |
| end |
| |
| default: begin |
| err_o = 1'b1; |
| err_en = On; |
| end |
| |
| endcase // unique case (state_q) |
| end |
| |
| |
| // 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] = tl_h2d_i; |
| tl_d2h_o = tl_d2h_int[0]; |
| tl_h2d_error = '0; |
| |
| if (lc_tx_test_true_loose(err_en)) begin |
| tl_h2d_error = tl_h2d_i; |
| tl_d2h_o = tl_d2h_error; |
| end |
| |
| if (block_cmd) begin |
| tl_d2h_o.a_ready = 1'b0; |
| tl_h2d_int[0].a_valid = 1'b0; |
| tl_h2d_error.a_valid = 1'b0; |
| 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) |
| ); |
| |
| // Add assertion |
| `ASSERT(OutStandingOvfl_A, &outstanding_txn |-> ~a_ack) |
| |
| endmodule : tlul_lc_gate |