1//
2// Copyright 2019 Ettus Research, A National Instruments Company
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: ctrlport_decoder
7//
8// Description:
9//
10// This block splits a single control port interface into multiple. It is used
11// when you have a single master that needs to access multiple slaves.  For
12// example, a NoC block where the registers are implemented in multiple
13// submodules that must be read/written by a single NoC shell.
14//
15// This version also implements address decoding. The request is passed to a
16// slave only if the address falls within that slave's address space. Each
17// slave is given an address space of 2**ADDR_W and the first slave starts at
18// address BASE_ADDR. In other words, the request address is partitioned as
19// shown below.
20//
21//   |---------------- 32-bit -----------------|
22//   |    Base     | Port Num |  Slave Addr    |
23//   |-----------------------------------------|
24//
25// When passed to the slave, the base address and port number bits are stripped
26// from the request address and only the SLAVE_ADDR_W-bit address is passed
27// through.
28//
29// Parameters:
30//
31//   NUM_SLAVES   : Number of slave devices that you want to connect to master.
32//   BASE_ADDR    : Base address for slave 0. This should be a power-of-2
33//                  multiple of the combined slave address spaces.
34//   SLAVE_ADDR_W : Number of address bits to allocate to each slave.
35//
36
37module ctrlport_decoder #(
38  parameter NUM_SLAVES   = 2,
39  parameter BASE_ADDR    = 0,
40  parameter SLAVE_ADDR_W = 8
41) (
42  input wire ctrlport_clk,
43  input wire ctrlport_rst,
44
45  // Slave Interface
46  input  wire        s_ctrlport_req_wr,
47  input  wire        s_ctrlport_req_rd,
48  input  wire [19:0] s_ctrlport_req_addr,
49  input  wire [31:0] s_ctrlport_req_data,
50  input  wire [ 3:0] s_ctrlport_req_byte_en,
51  input  wire        s_ctrlport_req_has_time,
52  input  wire [63:0] s_ctrlport_req_time,
53  output reg         s_ctrlport_resp_ack = 1'b0,
54  output reg  [ 1:0] s_ctrlport_resp_status,
55  output reg  [31:0] s_ctrlport_resp_data,
56
57  // Master Interfaces
58  output reg  [   NUM_SLAVES-1:0] m_ctrlport_req_wr = 0,
59  output reg  [   NUM_SLAVES-1:0] m_ctrlport_req_rd = 0,
60  output reg  [20*NUM_SLAVES-1:0] m_ctrlport_req_addr = 0,
61  output reg  [32*NUM_SLAVES-1:0] m_ctrlport_req_data,
62  output reg  [ 4*NUM_SLAVES-1:0] m_ctrlport_req_byte_en,
63  output reg  [   NUM_SLAVES-1:0] m_ctrlport_req_has_time,
64  output reg  [64*NUM_SLAVES-1:0] m_ctrlport_req_time,
65  input  wire [   NUM_SLAVES-1:0] m_ctrlport_resp_ack,
66  input  wire [ 2*NUM_SLAVES-1:0] m_ctrlport_resp_status,
67  input  wire [32*NUM_SLAVES-1:0] m_ctrlport_resp_data
68);
69
70  localparam        PORT_NUM_W      = $clog2(NUM_SLAVES);
71  localparam        PORT_NUM_POS    = SLAVE_ADDR_W;
72  localparam        BASE_ADDR_W     = 20 - (SLAVE_ADDR_W + PORT_NUM_W);
73  localparam        BASE_ADDR_POS   = SLAVE_ADDR_W + PORT_NUM_W;
74  localparam [19:0] BASE_ADDR_MASK  = { BASE_ADDR_W {1'b1}} << BASE_ADDR_POS;
75
76
77  //---------------------------------------------------------------------------
78  // Split the requests among the slaves
79  //---------------------------------------------------------------------------
80
81  wire [NUM_SLAVES-1:0] decoder;
82
83  generate
84    genvar i;
85    for (i = 0; i < NUM_SLAVES; i = i+1) begin : gen_split
86      // Check if the upper bits of the request address match each slave. If the
87      // address matches, set the corresponding decoder[] bit.
88      if (PORT_NUM_W == 0) begin
89        // Only one port in this case, so there are no port number bits to check
90        assign decoder[i] = ((s_ctrlport_req_addr & BASE_ADDR_MASK) == BASE_ADDR);
91      end else begin
92        assign decoder[i] = ((s_ctrlport_req_addr & BASE_ADDR_MASK) == BASE_ADDR) &&
93                             (s_ctrlport_req_addr[PORT_NUM_POS +: PORT_NUM_W] == i);
94      end
95
96      always @(posedge ctrlport_clk) begin
97        if (ctrlport_rst) begin
98          m_ctrlport_req_wr[i] <= 1'b0;
99          m_ctrlport_req_rd[i] <= 1'b0;
100        end else begin
101          // Mask WR and RD based on address decoding
102          m_ctrlport_req_wr[i] <= s_ctrlport_req_wr & decoder[i];
103          m_ctrlport_req_rd[i] <= s_ctrlport_req_rd & decoder[i];
104
105          // Other values pass through to all slaves, but should be ignored
106          // unless the corresponding WR or RD is not asserted.
107          m_ctrlport_req_data    [32*i +: 32] <= s_ctrlport_req_data;
108          m_ctrlport_req_byte_en [4*i +: 4]   <= s_ctrlport_req_byte_en;
109          m_ctrlport_req_has_time[i]          <= s_ctrlport_req_has_time;
110          m_ctrlport_req_time    [64*i +: 64] <= s_ctrlport_req_time;
111
112          // Pass through only the relevant slave bits
113          m_ctrlport_req_addr[20*i+:20]           <= 20'b0;
114          m_ctrlport_req_addr[20*i+:SLAVE_ADDR_W] <= s_ctrlport_req_addr[SLAVE_ADDR_W-1:0];
115        end
116      end
117    end
118  endgenerate
119
120
121  //---------------------------------------------------------------------------
122  // Decode the responses
123  //---------------------------------------------------------------------------
124
125  reg [31:0] data;
126  reg [ 1:0] status;
127  reg        ack = 0;
128
129  // Take the responses and mask them with ack, then OR them together
130  always @(*) begin : comb_decode
131    integer s;
132    data   = 0;
133    status = 0;
134    ack    = 0;
135    for (s = 0; s < NUM_SLAVES; s = s+1) begin
136      data   = data   | (m_ctrlport_resp_data  [s*32 +: 32] & {32{m_ctrlport_resp_ack[s]}});
137      status = status | (m_ctrlport_resp_status[s* 2 +:  2] & { 2{m_ctrlport_resp_ack[s]}});
138      ack    = ack    | m_ctrlport_resp_ack[s];
139    end
140  end
141
142  // Register the output to break combinatorial path
143  always @(posedge ctrlport_clk) begin
144    if (ctrlport_rst) begin
145      s_ctrlport_resp_ack  <= 0;
146    end else begin
147      s_ctrlport_resp_data   <= data;
148      s_ctrlport_resp_status <= status;
149      s_ctrlport_resp_ack    <= ack;
150    end
151  end
152
153endmodule
154