aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/settings_bus_timed_2clk.v
blob: 2b62ffd269e6a5ca31c1d05d40998fe8649bf646 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//
// Copyright 2018 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: settings_bus_timed_2clk
// Description:
// - Stores settings bus transaction in a FIFO and 
//   releases them based on VITA time input
// - Also moves the settings bus to the timebase
//   clock domain
//

module settings_bus_timed_2clk #(
  parameter SR_AWIDTH     = 8,
  parameter SR_DWIDTH     = 32,
  parameter RB_AWIDTH     = 8,
  parameter RB_DWIDTH     = 64,
  parameter TIMED_CMDS_EN = 0
) (
  input                  sb_clk,          // Settings bus clock
  input                  sb_rst,          // Reset (sb_clk)
  input                  tb_clk,          // Timebase clock
  input                  tb_rst,          // Reset (tb_clk)

  input  [63:0]          vita_time,       // Current timebase time
                         
  input                  s_set_stb,       // Settings bus strobe
  input  [SR_AWIDTH-1:0] s_set_addr,      // Settings address
  input  [SR_DWIDTH-1:0] s_set_data,      // Settings data
  input                  s_set_has_time,  // Is this a timed command?
  input  [63:0]          s_set_time,      // Command time
  output                 s_set_pending,   // Is settings transaction pending?
  input  [RB_AWIDTH-1:0] s_rb_addr,       // Readback address
  output                 s_rb_stb,        // Readback data strobe
  output [RB_DWIDTH-1:0] s_rb_data,       // Readback data value

  output                 m_set_stb,       // Settings bus strobe
  output [SR_AWIDTH-1:0] m_set_addr,      // Settings address
  output [SR_DWIDTH-1:0] m_set_data,      // Settings data
  output                 m_set_has_time,  // Is this a timed command?
  output [63:0]          m_set_time,      // Command time
  input                  m_set_pending,   // Is settings transaction pending?
  output [RB_AWIDTH-1:0] m_rb_addr,       // Readback address
  input                  m_rb_stb,        // Readback data strobe
  input  [RB_DWIDTH-1:0] m_rb_data        // Readback data value
);

  // States for input and output state machines
  localparam [2:0] ST_IDLE        = 3'd0; // Nothing is happening on the bus
  localparam [2:0] ST_SET_ISSUED  = 3'd1; // A settings transaction has been issued
  localparam [2:0] ST_SET_PENDING = 3'd2; // A settings transaction is pending
  localparam [2:0] ST_RB_PENDING  = 3'd3; // Waiting for readback data
  localparam [2:0] ST_RB_DONE     = 3'd4; // Readback data is valid
  
  wire rb_valid;

  // Input state machine
  reg [2:0] in_state = ST_IDLE;
  always @(posedge sb_clk) begin
    if (sb_rst) begin
      in_state <= ST_IDLE;
    end else begin
      case (in_state)
        ST_IDLE: begin
          if (s_set_stb) begin
            in_state <= ST_SET_PENDING;
          end
        end
        ST_SET_PENDING: begin
          if (rb_valid) begin
            in_state <= ST_RB_DONE;
          end
        end
        ST_RB_DONE: begin
          in_state <= ST_IDLE;
        end
        default: begin
          in_state <= ST_IDLE;
        end
      endcase
    end
  end
  assign s_set_pending = (in_state == ST_SET_PENDING);
  assign s_rb_stb = (in_state == ST_RB_DONE);

  // Clock crossing FIFO (settings)
  // TODO: Look into a more efficient implementation for a single element
  //       clock crossing FIFO.
  wire set_pending, set_finished;
  axi_fifo_2clk #(
    .WIDTH(SR_AWIDTH+SR_DWIDTH+1+64+RB_AWIDTH), .SIZE(0)
  ) sb_2clk_fifo_i (
    .i_aclk(sb_clk), .reset(sb_rst),
    .i_tdata({s_set_addr, s_set_data, s_set_has_time, s_set_time, s_rb_addr}),
    .i_tvalid(s_set_stb), .i_tready(/* Ignored: FIFO may not have an exact size*/),
    .o_aclk(tb_clk),
    .o_tdata({m_set_addr, m_set_data, m_set_has_time, m_set_time, m_rb_addr}),
    .o_tvalid(set_pending), .o_tready(set_finished)
  );

  // Time compare logic
  // If ~has_time then pass the transaction through, otherwise wait for time
  // to tick up to command time
  wire now, late;
  wire go = ((TIMED_CMDS_EN == 1) && m_set_has_time) ? (now | late) : 1'b1;

  // If this is a timed command then vita_time == m_set_time one cycle before
  // strobe is asserted i.e. timed strobe assertion has a one cycle latency
  time_compare time_compare (
    .clk(tb_clk), .reset(tb_rst),
    .time_now(vita_time), .trigger_time(m_set_time),
    .now(now), .early(), .late(late), .too_early()
  );

  // Clock crossing FIFO (readback)
  reg [RB_DWIDTH-1:0] cached_rb_data;
  axi_fifo_2clk #(
    .WIDTH(RB_DWIDTH), .SIZE(0)
  ) rbdata_2clk_fifo_i (
    .reset(tb_rst),
    .i_aclk(tb_clk), .i_tdata(cached_rb_data), .i_tvalid(set_finished), .i_tready(),
    .o_aclk(sb_clk), .o_tdata(s_rb_data), .o_tvalid(rb_valid), .o_tready(s_rb_stb)
  );

  // Output state machine
  reg [2:0] out_state = ST_IDLE;
  always @(posedge tb_clk) begin
    if (tb_rst) begin
      out_state <= ST_IDLE;
    end else begin
      case (out_state)
        ST_IDLE: begin
          if (go & set_pending) begin
            out_state <= ST_SET_ISSUED;
          end
        end
        ST_SET_ISSUED: begin
          out_state <= ST_SET_PENDING;
        end
        ST_SET_PENDING: begin
          if (~m_set_pending) begin
            if (m_rb_stb) begin
              out_state <= ST_RB_DONE;
              cached_rb_data <= m_rb_data;
            end else begin
              out_state <= ST_RB_PENDING;
            end
          end
        end
        ST_RB_PENDING: begin
          if (m_rb_stb) begin
            out_state <= ST_RB_DONE;
            cached_rb_data <= m_rb_data;
          end
        end
        ST_RB_DONE: begin
          out_state <= ST_IDLE;
        end
        default: begin
          out_state <= ST_IDLE;
        end
      endcase
    end
  end

  assign m_set_stb = (out_state == ST_SET_ISSUED);
  assign set_finished = (out_state == ST_RB_DONE);

endmodule