1//
2// Copyright 2013 Ettus Research LLC
3// Copyright 2018 Ettus Research, a National Instruments Company
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6//
7
8
9`define BIT_WIDTH(N) (\
10                 N <= 2    ? 1 : \
11                 N <= 4    ? 2 : \
12                 N <= 8    ? 3 : \
13                 N <= 16   ? 4 : \
14                 N <= 32   ? 5 : \
15                 N <= 64   ? 6 : \
16                 N <= 128  ? 7 : \
17                 N <= 256  ? 8 : \
18                 N <= 512  ? 9 : \
19                 10)
20`define GET_REG_OFFSET(reg_addr, chan_idx) (((chan_idx * (1<<DMA_REG_GRP_W)) + reg_addr) + REG_BASE_ADDR)
21`define EXTRACT_CHAN_NUM(reg_addr) regi_addr[`BIT_WIDTH(NUM_STREAMS)+DMA_REG_GRP_W-1:DMA_REG_GRP_W]
22
23module pcie_dma_ctrl #(
24    parameter NUM_STREAMS   = 4,
25    parameter FRAME_SIZE_W  = 16,
26    parameter REG_BASE_ADDR = 20'h00000,
27    parameter ENABLE_ROUTER = 0,
28    parameter ROUTER_SID_W  = 8,
29    parameter ROUTER_DST_W  = 2
30) (
31    input           clk,
32    input           reset,
33
34    input [63:0]    regi_tdata,
35    input           regi_tvalid,
36    output          regi_tready,
37    output [63:0]   rego_tdata,
38    output          rego_tvalid,
39    input           rego_tready,
40
41    output reg [NUM_STREAMS-1:0]            set_enabled,
42    output reg [NUM_STREAMS-1:0]            set_clear,
43    output [(NUM_STREAMS*FRAME_SIZE_W)-1:0] set_frame_size,
44
45    input  [NUM_STREAMS-1:0]                packet_stb,
46    input  [NUM_STREAMS-1:0]                sample_stb,
47    input  [NUM_STREAMS-1:0]                stream_busy,
48    input  [NUM_STREAMS-1:0]                stream_err,
49
50    input  [ROUTER_SID_W-1:0]               rtr_sid,
51    output [ROUTER_DST_W-1:0]               rtr_dst
52);
53
54    localparam DMA_REG_GRP_W        = 4;
55    localparam DMA_CTRL_STATUS_REG  = 4'h0; //[RW] R: Stream Status, W: Stream Control
56    localparam DMA_FSIZE_REG        = 4'h4; //[RW] R: Frame Size, W: Frame Size
57    localparam DMA_SAMP_CNT_REG     = 4'h8; //[RW] R: Sample Count, W: Reset Count to 0
58    localparam DMA_PKT_CNT_REG      = 4'hC; //[RW] R: Packet Count, W: Reset Count to 0
59
60    localparam DEFAULT_FSIZE        = 32;
61
62    //NOTE: Although this module supports these, the 8 and 16 bit modes will be disabled for efficiency
63    localparam DMA_CTRL_BUF_SIZE_8  = 2'b00;    // 8-bit wide SW buffer
64    localparam DMA_CTRL_BUF_SIZE_16 = 2'b01;    //16-bit wide SW buffer
65    localparam DMA_CTRL_BUF_SIZE_32 = 2'b10;    //32-bit wide SW buffer
66    localparam DMA_CTRL_BUF_SIZE_64 = 2'b11;    //64-bit wide SW buffer
67
68    wire            regi_wr, regi_rd;
69    wire [19:0]     regi_addr;
70    wire [31:0]     regi_payload;
71    wire [31:0]     rego_payload;
72
73    ioport2_msg_decode regi_decoder (
74        .message(regi_tdata), .wr_request(regi_wr), .rd_request(regi_rd),
75        .address(regi_addr), .data(regi_payload)
76    );
77
78    ioport2_msg_encode  rego_encoder (
79        .rd_response(1'b1), .data(rego_payload), .message(rego_tdata)
80    );
81
82    reg [31:0]              pkt_count_mem[0:NUM_STREAMS-1];
83    reg [31:0]              samp_count_mem[0:NUM_STREAMS-1];
84    reg [FRAME_SIZE_W-1:0]  frame_size_mem[0:NUM_STREAMS-1];
85
86    genvar i;
87    generate
88        for (i=0; i<NUM_STREAMS; i=i+1) begin: dma_ctrl_logic_generator
89            //Memory -> output translations
90            assign set_frame_size[(FRAME_SIZE_W*(i+1))-1:(FRAME_SIZE_W*i)] = frame_size_mem[i];
91
92            //Setting registers
93            always @(posedge clk) begin
94                if (reset) begin
95                    frame_size_mem[i] <= DEFAULT_FSIZE;
96                    set_clear[i] <= 0;
97                    set_enabled[i] <= 0;
98                end else if (regi_tready & regi_tvalid & regi_wr) begin
99                    if (regi_addr == `GET_REG_OFFSET(DMA_CTRL_STATUS_REG, i)) begin
100                        set_clear[i]        <= regi_payload[0];                 //DMA_CTRL_STATUS_REG[0] == Clear DMA queues
101                        set_enabled[i]      <= regi_payload[1];                 //DMA_CTRL_STATUS_REG[1] == Enable DMA channel
102                    end else if (regi_addr == `GET_REG_OFFSET(DMA_FSIZE_REG, i)) begin
103                        frame_size_mem[i] <= regi_payload[FRAME_SIZE_W-1:0];    //DMA_FSIZE_REG[14:0] == DMA Frame size
104                    end
105                end else begin
106                    set_clear[i] <= 0;                                          //set_clear should be "self-clearing"
107                end
108            end
109
110            //Packet counter
111            always @(posedge clk) begin
112                if (reset | (regi_tvalid && regi_wr && (regi_addr == `GET_REG_OFFSET(DMA_PKT_CNT_REG, i)))) begin
113                    pkt_count_mem[i] <= 0;
114                end else if (packet_stb[i]) begin
115                    pkt_count_mem[i] <= pkt_count_mem[i] + 1;
116                end
117            end
118
119            //Sample counter
120            always @(posedge clk) begin
121                if (reset | (regi_tvalid && regi_wr && (regi_addr == `GET_REG_OFFSET(DMA_SAMP_CNT_REG, i)))) begin
122                    samp_count_mem[i] <= 0;
123                end else if (sample_stb[i]) begin
124                    samp_count_mem[i] <= samp_count_mem[i] + 1;
125                end
126            end
127        end
128    endgenerate
129
130    //Readback
131    assign rego_payload =
132        (regi_addr[DMA_REG_GRP_W-1:0] == DMA_PKT_CNT_REG)     ?      pkt_count_mem[`EXTRACT_CHAN_NUM(regi_addr)]  : (
133        (regi_addr[DMA_REG_GRP_W-1:0] == DMA_SAMP_CNT_REG)    ?     samp_count_mem[`EXTRACT_CHAN_NUM(regi_addr)]  : (
134        (regi_addr[DMA_REG_GRP_W-1:0] == DMA_FSIZE_REG)       ?     frame_size_mem[`EXTRACT_CHAN_NUM(regi_addr)]  : (
135        (regi_addr[DMA_REG_GRP_W-1:0] == DMA_CTRL_STATUS_REG) ? {30'h0, stream_busy[`EXTRACT_CHAN_NUM(regi_addr)], stream_err[`EXTRACT_CHAN_NUM(regi_addr)]} : (
136        32'hFFFFFFFF))));
137
138    assign rego_tvalid = regi_tvalid && regi_rd;
139    assign regi_tready = rego_tready || (regi_tvalid && regi_wr);
140
141    //Optional router
142    generate if (ENABLE_ROUTER == 1) begin
143        pcie_pkt_route_specifier #(
144            .BASE_ADDR((1<<ROUTER_SID_W) + REG_BASE_ADDR), .ADDR_MASK(20'hFFFFF^((1<<ROUTER_SID_W)-1)),
145            .SID_WIDTH(ROUTER_SID_W), .DST_WIDTH(ROUTER_DST_W)
146        ) route_specifier (
147            .clk(clk), .reset(reset),
148            .regi_tdata(regi_tdata), .regi_tvalid(regi_tvalid), .regi_tready(),
149            .local_sid(rtr_sid), .fifo_dst(rtr_dst)
150        );
151    end endgenerate
152
153endmodule
154
155`undef EXTRACT_CHAN_NUM
156`undef GET_REG_OFFSET
157`undef BIT_WIDTH
158