108 lines
3.8 KiB
Systemverilog
108 lines
3.8 KiB
Systemverilog
//
|
|
// Copyright 2025 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
//
|
|
// Module: ctrlport_if_decoder
|
|
//
|
|
// Description:
|
|
//
|
|
// This block splits a single control port interface into multiple. It is
|
|
// used when you have a single master that needs to access multiple slaves.
|
|
//
|
|
// This version also implements address decoding. The request is passed to a
|
|
// slave only if the address falls within that slave's address space. Each
|
|
// slave can have a unique base address and address space size.
|
|
//
|
|
// When passed to the slave, the base address is subtracted from the request
|
|
// address.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// NUM_SLAVES : The number of slaves to connect to a master.
|
|
// PORT_BASE : Base addresses to use for each slave.
|
|
// PORT_SIZE : Size of the address space for each slave.
|
|
//
|
|
|
|
module ctrlport_if_decoder #(
|
|
int NUM_SLAVES = 2,
|
|
int PORT_BASE [NUM_SLAVES] = '{'h0, 'h100},
|
|
int PORT_SIZE [NUM_SLAVES] = '{'h100, 'h100}
|
|
) (
|
|
// Slave Interface
|
|
ctrlport_if.slave s_ctrlport,
|
|
// Master Interfaces
|
|
ctrlport_if.master m_ctrlport [NUM_SLAVES]
|
|
);
|
|
|
|
import ctrlport_pkg::*;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Check the address ranges
|
|
//---------------------------------------------------------------------------
|
|
for (genvar i = 0; i < NUM_SLAVES; i = i+1) begin : gen_overlap_1
|
|
for (genvar j = 0; j < NUM_SLAVES; j = j+1) begin : gen_overlap_2
|
|
if (i != j) begin
|
|
if ((PORT_BASE[i] >= PORT_BASE[j]) &&
|
|
(PORT_BASE[i] < PORT_BASE[j] + PORT_SIZE[j])) begin
|
|
$error("Port %0d overlaps with port %0d.", i, j);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Split the requests among the slaves
|
|
//---------------------------------------------------------------------------
|
|
for (genvar i = 0; i < NUM_SLAVES; i = i+1) begin : gen_split
|
|
always_ff @(posedge s_ctrlport.clk) begin
|
|
// unconditionally pass the request by default
|
|
m_ctrlport[i].req <= s_ctrlport.req;
|
|
// pass only the respective address bits
|
|
m_ctrlport[i].req.addr <= '0;
|
|
m_ctrlport[i].req.addr[$clog2(PORT_SIZE[i])-1:0] <= s_ctrlport.req.addr - PORT_BASE[i];
|
|
|
|
// read and write trigger transactions and therefore need to react to reset
|
|
if (s_ctrlport.rst) begin
|
|
m_ctrlport[i].req.wr <= 1'b0;
|
|
m_ctrlport[i].req.rd <= 1'b0;
|
|
end else begin
|
|
automatic logic address_in_range;
|
|
address_in_range = (s_ctrlport.req.addr >= PORT_BASE[i]) &&
|
|
(s_ctrlport.req.addr < PORT_BASE[i] + PORT_SIZE[i]);
|
|
m_ctrlport[i].req.wr <= s_ctrlport.req.wr & address_in_range;
|
|
m_ctrlport[i].req.rd <= s_ctrlport.req.rd & address_in_range;
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Decode the responses
|
|
//---------------------------------------------------------------------------
|
|
// Take the responses and mask them with their respective ack
|
|
ctrlport_response_t masked_resp [NUM_SLAVES-1:0];
|
|
for (genvar i = 0; i < NUM_SLAVES; i++) begin : gen_mask
|
|
assign masked_resp[i] = m_ctrlport[i].resp.ack ? m_ctrlport[i].resp : '0;
|
|
end
|
|
|
|
// Combine the masked responses by OR'ing them together
|
|
ctrlport_response_t combined_resp;
|
|
always_comb begin : response_combine
|
|
combined_resp = '0;
|
|
for (int i = 0; i < NUM_SLAVES; i++) begin : gen_or
|
|
combined_resp = combined_resp | masked_resp[i];
|
|
end
|
|
end
|
|
|
|
// Register the output to break combinatorial path
|
|
always_ff @(posedge s_ctrlport.clk) begin : response_reg
|
|
s_ctrlport.resp <= combined_resp;
|
|
|
|
if (s_ctrlport.rst) begin
|
|
s_ctrlport.resp.ack <= '0;
|
|
end
|
|
end
|
|
|
|
endmodule
|