blob: ac487501290251e5e377481a54b4c9a68da24b3c [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "prim_assert.sv"
/**
* Tile-Link UL adapter for Register interface
*
* TODO(#15822): Note that due to some modules with special needs (like
* the vendored-in RV_DM), this module has been extended so that it
* supports use cases outside of the generated reg_top module. This makes
* this adapter and its parameterization options a bit heavy.
*
* We should in the future come back to this and refactor / align the
* module and its parameterization needs.
*/
module tlul_adapter_reg
import tlul_pkg::*;
import prim_mubi_pkg::mubi4_t;
#(
parameter bit CmdIntgCheck = 0, // 1: Enable command integrity check
parameter bit EnableRspIntgGen = 0, // 1: Generate response integrity
parameter bit EnableDataIntgGen = 0, // 1: Generate response data integrity
parameter int RegAw = 8, // Width of register address
parameter int RegDw = 32, // Shall be matched with TL_DW
parameter int AccessLatency = 0, // 0: same cycle, 1: next cycle
localparam int RegBw = RegDw/8
) (
input clk_i,
input rst_ni,
// TL-UL interface
input tl_h2d_t tl_i,
output tl_d2h_t tl_o,
// control interface
input mubi4_t en_ifetch_i,
output logic intg_error_o,
// Register interface
output logic re_o,
output logic we_o,
output logic [RegAw-1:0] addr_o,
output logic [RegDw-1:0] wdata_o,
output logic [RegBw-1:0] be_o,
input busy_i,
// The following two signals are expected
// to be returned in AccessLatency cycles.
input [RegDw-1:0] rdata_i,
// This can be a write or read error.
input error_i
);
`ASSERT_INIT(AllowedLatency_A, AccessLatency inside {0, 1})
localparam int IW = $bits(tl_i.a_source);
localparam int SZW = $bits(tl_i.a_size);
logic outstanding_q; // Indicates current request is pending
logic a_ack, d_ack;
logic [RegDw-1:0] rdata, rdata_q;
logic error_q, error, err_internal, instr_error, intg_error;
logic addr_align_err; // Size and alignment
logic malformed_meta_err; // User signal format error or unsupported
logic tl_err; // Common TL-UL error checker
logic [IW-1:0] reqid_q;
logic [SZW-1:0] reqsz_q;
tl_d_op_e rspop_q;
logic rd_req, wr_req;
assign a_ack = tl_i.a_valid & tl_o.a_ready;
assign d_ack = tl_o.d_valid & tl_i.d_ready;
// Request signal
assign wr_req = a_ack & ((tl_i.a_opcode == PutFullData) | (tl_i.a_opcode == PutPartialData));
assign rd_req = a_ack & (tl_i.a_opcode == Get);
assign we_o = wr_req & ~err_internal;
assign re_o = rd_req & ~err_internal;
assign wdata_o = tl_i.a_data;
assign be_o = tl_i.a_mask;
if (RegAw <= 2) begin : gen_only_one_reg
assign addr_o = '0;
end else begin : gen_more_regs
assign addr_o = {tl_i.a_address[RegAw-1:2], 2'b00}; // generate always word-align
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) outstanding_q <= 1'b0;
else if (a_ack) outstanding_q <= 1'b1;
else if (d_ack) outstanding_q <= 1'b0;
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
reqid_q <= '0;
reqsz_q <= '0;
rspop_q <= AccessAck;
end else if (a_ack) begin
reqid_q <= tl_i.a_source;
reqsz_q <= tl_i.a_size;
// Return AccessAckData regardless of error
rspop_q <= (rd_req) ? AccessAckData : AccessAck ;
end
end
if (AccessLatency == 1) begin : gen_access_latency1
logic wr_req_q, rd_req_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rdata_q <= '0;
error_q <= 1'b0;
wr_req_q <= 1'b0;
rd_req_q <= 1'b0;
end else begin
rd_req_q <= rd_req;
wr_req_q <= wr_req;
// Addressing phase
if (a_ack) begin
error_q <= err_internal;
// Response phase
end else begin
error_q <= error;
rdata_q <= rdata;
end
end
end
assign rdata = (error_i || error_q || wr_req_q) ? '1 :
(rd_req_q) ? rdata_i :
rdata_q; // backpressure case
assign error = (rd_req_q || wr_req_q) ? (error_q || error_i) :
error_q; // backpressure case
end else begin : gen_access_latency0
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rdata_q <= '0;
error_q <= 1'b0;
end else if (a_ack) begin
rdata_q <= (error_i || err_internal || wr_req) ? '1 : rdata_i;
error_q <= error_i || err_internal;
end
end
assign rdata = rdata_q;
assign error = error_q;
end
tlul_pkg::tl_d2h_t tl_o_pre;
assign tl_o_pre = '{
// busy is selected based on address
// thus if there is no valid transaction, we should ignore busy
a_ready: ~(outstanding_q | tl_i.a_valid & busy_i),
d_valid: outstanding_q,
d_opcode: rspop_q,
d_param: '0,
d_size: reqsz_q,
d_source: reqid_q,
d_sink: '0,
d_data: rdata,
d_user: '0,
d_error: error
};
// outgoing integrity generation
tlul_rsp_intg_gen #(
.EnableRspIntgGen(EnableRspIntgGen),
.EnableDataIntgGen(EnableDataIntgGen)
) u_rsp_intg_gen (
.tl_i(tl_o_pre),
.tl_o(tl_o)
);
if (CmdIntgCheck) begin : gen_cmd_intg_check
logic intg_error_q;
tlul_cmd_intg_chk u_cmd_intg_chk (
.tl_i(tl_i),
.err_o(intg_error)
);
// permanently latch integrity error until reset
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
intg_error_q <= 1'b0;
end else if (intg_error) begin
intg_error_q <= 1'b1;
end
end
assign intg_error_o = intg_error_q;
end else begin : gen_no_cmd_intg_check
assign intg_error = 1'b0;
assign intg_error_o = 1'b0;
end
////////////////////
// Error Handling //
////////////////////
// An instruction type transaction is only valid if en_ifetch is enabled
// If the instruction type is completely invalid, also considered an instruction error
assign instr_error = prim_mubi_pkg::mubi4_test_invalid(tl_i.a_user.instr_type) |
(prim_mubi_pkg::mubi4_test_true_strict(tl_i.a_user.instr_type) &
prim_mubi_pkg::mubi4_test_false_loose(en_ifetch_i));
assign err_internal = addr_align_err | malformed_meta_err | tl_err | instr_error | intg_error;
// Don't allow unsupported values.
assign malformed_meta_err = tl_a_user_chk(tl_i.a_user);
// addr_align_err
// Raised if addr isn't aligned with the size
// Read size error is checked in tlul_assert.sv
// Here is it added due to the limitation of register interface.
always_comb begin
if (wr_req) begin
// Only word-align is accepted based on comportability spec
addr_align_err = |tl_i.a_address[1:0];
end else begin
// No request
addr_align_err = 1'b0;
end
end
// tl_err : separate checker
tlul_err u_err (
.clk_i,
.rst_ni,
.tl_i,
.err_o (tl_err)
);
`ASSERT_INIT(MatchedWidthAssert, RegDw == top_pkg::TL_DW)
endmodule