| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| |
| package tlul_pkg; |
| |
| // this can be either PPC or BINTREE |
| // there is no functional difference, but timing and area behavior is different |
| // between the two instances. PPC can result in smaller implementations when timing |
| // is not critical, whereas BINTREE is favorable when timing pressure is high (but this |
| // may also result in a larger implementation). on FPGA targets, BINTREE is favorable |
| // both in terms of area and timing. |
| parameter ArbiterImpl = "PPC"; |
| |
| // TODO(hoangm): Below enums have been modified to support TL-UH. Eventually |
| // we'll want to upstream these changes to OT. |
| typedef enum logic [2:0] { |
| PutFullData = 3'h 0, |
| PutPartialData = 3'h 1, |
| ArithmeticData = 3'h 2, |
| LogicalData = 3'h 3, |
| Get = 3'h 4, |
| Intent = 3'h 5 |
| } tl_a_op_e; |
| |
| typedef enum logic [2:0] { |
| AccessAck = 3'h 0, |
| AccessAckData = 3'h 1, |
| HintAck = 3'h 2 |
| } tl_d_op_e; |
| |
| typedef enum logic [2:0] { |
| Min = 3'h 0, |
| Max = 3'h 1, |
| MinU = 3'h 2, |
| MaxU = 3'h 3, |
| Add = 3'h 4 |
| } tl_a_arith_param_e; |
| |
| typedef enum logic [2:0] { |
| Xor = 3'h 0, |
| Or = 3'h 1, |
| And = 3'h 2, |
| Swap = 3'h 3 |
| } tl_a_logical_param_e; |
| |
| typedef enum logic [2:0] { |
| PrefetchRead = 3'h 0, |
| PrefetchWrite = 3'h 1 |
| } tl_a_intent_param_e; |
| |
| parameter int H2DCmdMaxWidth = 57; |
| parameter int H2DCmdIntgWidth = 7; |
| parameter int H2DCmdFullWidth = H2DCmdMaxWidth + H2DCmdIntgWidth; |
| parameter int D2HRspMaxWidth = 57; |
| parameter int D2HRspIntgWidth = 7; |
| parameter int D2HRspFullWidth = D2HRspMaxWidth + D2HRspIntgWidth; |
| parameter int DataMaxWidth = 32; |
| parameter int DataIntgWidth = 7; |
| parameter int DataFullWidth = DataMaxWidth + DataIntgWidth; |
| |
| // Data that is returned upon an a TL-UL error belonging to an instruction fetch. |
| // Note that this data will be returned with the correct bus integrity value. |
| parameter logic [top_pkg::TL_DW-1:0] DataWhenInstrError = '0; |
| // Data that is returned upon an a TL-UL error not belonging to an instruction fetch. |
| // Note that this data will be returned with the correct bus integrity value. |
| parameter logic [top_pkg::TL_DW-1:0] DataWhenError = {top_pkg::TL_DW{1'b1}}; |
| |
| typedef struct packed { |
| logic [4:0] rsvd; |
| prim_mubi_pkg::mubi4_t instr_type; |
| logic [H2DCmdIntgWidth-1:0] cmd_intg; |
| logic [DataIntgWidth-1:0] data_intg; |
| } tl_a_user_t; |
| |
| parameter tl_a_user_t TL_A_USER_DEFAULT = '{ |
| rsvd: '0, |
| instr_type: prim_mubi_pkg::MuBi4False, |
| cmd_intg: {H2DCmdIntgWidth{1'b1}}, |
| data_intg: {DataIntgWidth{1'b1}} |
| }; |
| |
| typedef struct packed { |
| prim_mubi_pkg::mubi4_t instr_type; |
| logic [top_pkg::TL_AW-1:0] addr; |
| tl_a_op_e opcode; |
| logic [top_pkg::TL_DBW-1:0] mask; |
| } tl_h2d_cmd_intg_t; |
| |
| typedef struct packed { |
| logic a_valid; |
| tl_a_op_e a_opcode; |
| logic [2:0] a_param; |
| logic [top_pkg::TL_SZW-1:0] a_size; |
| logic [top_pkg::TL_AIW-1:0] a_source; |
| logic [top_pkg::TL_AW-1:0] a_address; |
| logic [top_pkg::TL_DBW-1:0] a_mask; |
| logic [top_pkg::TL_DW-1:0] a_data; |
| tl_a_user_t a_user; |
| |
| logic d_ready; |
| } tl_h2d_t; |
| |
| // The choice of all 1's as the blanked value is deliberate. |
| // It is assumed that most security features of the design are opt-in instead |
| // of opt-out. |
| // Given the opt-in nature, if a 0 were to propagate, the feature would be turned |
| // off. Whereas if a 1 were to propagate, it would either stay on or be turned on. |
| // There is however no perfect value for this purpose. |
| localparam logic [top_pkg::TL_DW-1:0] BlankedAData = {top_pkg::TL_DW{1'b1}}; |
| |
| localparam tl_h2d_t TL_H2D_DEFAULT = '{ |
| d_ready: 1'b1, |
| a_opcode: tl_a_op_e'('0), |
| a_user: TL_A_USER_DEFAULT, |
| a_data: BlankedAData, |
| default: '0 |
| }; |
| |
| typedef struct packed { |
| logic [D2HRspIntgWidth-1:0] rsp_intg; |
| logic [DataIntgWidth-1:0] data_intg; |
| } tl_d_user_t; |
| |
| parameter tl_d_user_t TL_D_USER_DEFAULT = '{ |
| rsp_intg: {D2HRspIntgWidth{1'b1}}, |
| data_intg: {DataIntgWidth{1'b1}} |
| }; |
| |
| typedef struct packed { |
| logic d_valid; |
| tl_d_op_e d_opcode; |
| logic [2:0] d_param; |
| logic [top_pkg::TL_SZW-1:0] d_size; // Bouncing back a_size |
| logic [top_pkg::TL_AIW-1:0] d_source; |
| logic [top_pkg::TL_DIW-1:0] d_sink; |
| logic [top_pkg::TL_DW-1:0] d_data; |
| tl_d_user_t d_user; |
| logic d_error; |
| |
| logic a_ready; |
| |
| } tl_d2h_t; |
| |
| typedef struct packed { |
| tl_d_op_e opcode; |
| logic [top_pkg::TL_SZW-1:0] size; |
| // Temporarily removed because source changes throughout the fabric |
| // and thus cannot be used for end-to-end checking. |
| // A different PR will propose a work-around (a hoaky one) to see if |
| // it gets the job done. |
| //logic [top_pkg::TL_AIW-1:0] source; |
| logic error; |
| } tl_d2h_rsp_intg_t; |
| |
| localparam tl_d2h_t TL_D2H_DEFAULT = '{ |
| a_ready: 1'b1, |
| d_opcode: tl_d_op_e'('0), |
| d_user: TL_D_USER_DEFAULT, |
| default: '0 |
| }; |
| |
| // Check user for unsupported values |
| function automatic logic tl_a_user_chk(tl_a_user_t user); |
| logic malformed_err; |
| logic unused_user; |
| unused_user = |user; |
| malformed_err = prim_mubi_pkg::mubi4_test_invalid(user.instr_type); |
| return malformed_err; |
| endfunction // tl_a_user_chk |
| |
| // extract variables used for command checking |
| function automatic tl_h2d_cmd_intg_t extract_h2d_cmd_intg(tl_h2d_t tl); |
| tl_h2d_cmd_intg_t payload; |
| logic unused_tlul; |
| unused_tlul = ^tl; |
| payload.addr = tl.a_address; |
| payload.opcode = tl.a_opcode; |
| payload.mask = tl.a_mask; |
| payload.instr_type = tl.a_user.instr_type; |
| return payload; |
| endfunction // extract_h2d_payload |
| |
| // extract variables used for response checking |
| function automatic tl_d2h_rsp_intg_t extract_d2h_rsp_intg(tl_d2h_t tl); |
| tl_d2h_rsp_intg_t payload; |
| logic unused_tlul; |
| unused_tlul = ^tl; |
| payload.opcode = tl.d_opcode; |
| payload.size = tl.d_size; |
| //payload.source = tl.d_source; |
| payload.error = tl.d_error; |
| return payload; |
| endfunction // extract_d2h_rsp_intg |
| |
| // calculate ecc for command checking |
| function automatic logic [H2DCmdIntgWidth-1:0] get_cmd_intg(tl_h2d_t tl); |
| logic [H2DCmdIntgWidth-1:0] cmd_intg; |
| logic [H2DCmdMaxWidth-1:0] unused_cmd_payload; |
| tl_h2d_cmd_intg_t cmd; |
| cmd = extract_h2d_cmd_intg(tl); |
| {cmd_intg, unused_cmd_payload} = |
| prim_secded_pkg::prim_secded_inv_64_57_enc(H2DCmdMaxWidth'(cmd)); |
| return cmd_intg; |
| endfunction // get_cmd_intg |
| |
| // calculate ecc for data checking |
| function automatic logic [DataIntgWidth-1:0] get_data_intg(logic [top_pkg::TL_DW-1:0] data); |
| logic [DataIntgWidth-1:0] data_intg; |
| logic [top_pkg::TL_DW-1:0] unused_data; |
| logic [DataIntgWidth + top_pkg::TL_DW - 1 : 0] enc_data; |
| enc_data = prim_secded_pkg::prim_secded_inv_39_32_enc(data); |
| data_intg = enc_data[DataIntgWidth + top_pkg::TL_DW - 1 : top_pkg::TL_DW]; |
| unused_data = enc_data[top_pkg::TL_DW - 1 : 0]; |
| return data_intg; |
| endfunction // get_data_intg |
| |
| // return inverted integrity for command payload |
| function automatic logic [H2DCmdIntgWidth-1:0] get_bad_cmd_intg(tl_h2d_t tl); |
| logic [H2DCmdIntgWidth-1:0] cmd_intg; |
| cmd_intg = get_cmd_intg(tl); |
| return ~cmd_intg; |
| endfunction // get_bad_cmd_intg |
| |
| // return inverted integrity for data payload |
| function automatic logic [H2DCmdIntgWidth-1:0] get_bad_data_intg(logic [top_pkg::TL_DW-1:0] data); |
| logic [H2DCmdIntgWidth-1:0] data_intg; |
| data_intg = get_data_intg(data); |
| return ~data_intg; |
| endfunction // get_bad_data_intg |
| |
| endpackage |