1
2//
3// Copyright 2011 Ettus Research LLC
4// Copyright 2018 Ettus Research, a National Instruments Company
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7//
8
9module gpio_atr #(
10  parameter BASE          = 0,
11  parameter WIDTH         = 32,
12  parameter FAB_CTRL_EN   = 0,
13  parameter DEFAULT_DDR   = 0,
14  parameter DEFAULT_IDLE  = 0
15) (
16  input clk, input reset,                                       //Clock and reset
17  input set_stb, input [7:0] set_addr, input [31:0] set_data,   //Settings control interface
18  input rx, input tx,                                           //Run signals that indicate tx and rx operation
19  input      [WIDTH-1:0]  gpio_in,                              //GPIO input state
20  output reg [WIDTH-1:0]  gpio_out,                             //GPIO output state
21  output reg [WIDTH-1:0]  gpio_ddr,                             //GPIO direction (0=input, 1=output)
22  input      [WIDTH-1:0]  gpio_out_fab,                         //GPIO driver bus from fabric
23  output reg [WIDTH-1:0]  gpio_sw_rb                            //Readback value for software
24);
25  genvar i;
26
27  wire [WIDTH-1:0]   in_idle, in_tx, in_rx, in_fdx, ddr_reg, atr_disable, fabric_ctrl;
28  reg [WIDTH-1:0]    ogpio, igpio;
29
30  setting_reg #(.my_addr(BASE+0), .width(WIDTH), .at_reset(DEFAULT_IDLE)) reg_idle (
31    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
32    .out(in_idle),.changed());
33
34  setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx (
35    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
36    .out(in_rx),.changed());
37
38  setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx (
39    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
40    .out(in_tx),.changed());
41
42  setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx (
43    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
44    .out(in_fdx),.changed());
45
46  setting_reg #(.my_addr(BASE+4), .width(WIDTH), .at_reset(DEFAULT_DDR)) reg_ddr (
47    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
48    .out(ddr_reg),.changed());
49
50  setting_reg #(.my_addr(BASE+5), .width(WIDTH)) reg_atr_disable (
51    .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
52    .out(atr_disable),.changed());
53
54  generate if (FAB_CTRL_EN == 1) begin
55    setting_reg #(.my_addr(BASE+6), .width(WIDTH)) reg_fabric_ctrl (
56      .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
57      .out(fabric_ctrl),.changed());
58  end else begin
59    assign fabric_ctrl = {WIDTH{1'b0}};
60  end endgenerate
61
62  //Pipeline rx and tx signals for easier timing closure
63  reg rx_d, tx_d;
64  always @(posedge clk)
65    {rx_d, tx_d} <= {rx, tx};
66
67  generate for (i=0; i<WIDTH; i=i+1) begin: gpio_mux_gen
68    //ATR selection MUX
69    always @(posedge clk) begin
70      case({atr_disable[i], tx_d, rx_d})
71        3'b000:   ogpio[i] <= in_idle[i];
72        3'b001:   ogpio[i] <= in_rx[i];
73        3'b010:   ogpio[i] <= in_tx[i];
74        3'b011:   ogpio[i] <= in_fdx[i];
75        default:  ogpio[i] <= in_idle[i];   //If ATR mode is disabled, always use IDLE value
76      endcase
77    end
78
79   //Pipeline input, output and direction
80   //For fabric access, insert MUX as close to the IO as possible
81   always @(posedge clk) begin
82     gpio_out[i] <= fabric_ctrl[i] ? gpio_out_fab[i] : ogpio[i];
83   end
84  end endgenerate
85
86  always @(posedge clk)
87    igpio <= gpio_in;
88
89  always @(posedge clk)
90    gpio_ddr <= ddr_reg;
91
92  //Generate software readback state
93  generate for (i=0; i<WIDTH; i=i+1) begin: gpio_rb_gen
94    always @(posedge clk)
95      gpio_sw_rb[i] <= gpio_ddr[i] ? gpio_out[i] : igpio[i];
96  end endgenerate
97
98endmodule // gpio_atr
99