1//
2// Copyright 2018 Ettus Research, A National Instruments Company
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: ctrlport_timer
7// Description:
8//   The Control-Port timer module converts an asynchronous timed
9//   transaction into a synchronous blocking transaction. This
10//   module will use the input req_has_time and req_time fields and
11//   produce an output transaction that will execute when the requested
12//   time is current. The module does not pass the has_time and time
13//   signals out because they are no longer relevant. The current time
14//   is an input to this module, and must be a monotonic counter that
15//   updates every time the time strobe is asserted.
16//
17// Parameters:
18//   - PRECISION_BITS : The number of bits to ignore when performing a
19//                      time comparison to determine execution time.
20//   - EXEC_LATE_CMDS : If a command is late, a TSERR response is sent.
21//                      If EXEC_LATE_CMDS = 1, then the late command will
22//                      be passed to the output regardless of the TSERR.
23//
24// Signals:
25//   - time_now*      : The time_now signal is the current time and the stb
26//                      signal indicates that the time_now is valid.
27//   - s_ctrlport_*   : The slave Control-Port bus.
28//                      This must have the has_time and time signals.
29//   - m_ctrlport_*   : The master Control-Port bus.
30//                      This will not have the has_time and time signals.
31
32module ctrlport_timer #(
33  parameter       PRECISION_BITS = 0,
34  parameter [0:0] EXEC_LATE_CMDS = 0
35)(
36  // Clocks and Resets
37  input  wire         clk,
38  input  wire         rst,
39  // Timestamp (synchronous to clk)
40  input  wire [63:0]  time_now,
41  input  wire         time_now_stb,
42  // Control Port Master (Request)
43  input  wire         s_ctrlport_req_wr,
44  input  wire         s_ctrlport_req_rd,
45  input  wire [19:0]  s_ctrlport_req_addr,
46  input  wire [31:0]  s_ctrlport_req_data,
47  input  wire [3:0]   s_ctrlport_req_byte_en,
48  input  wire         s_ctrlport_req_has_time,
49  input  wire [63:0]  s_ctrlport_req_time,
50  // Control Port Slave (Response)
51  output wire         s_ctrlport_resp_ack,
52  output wire [1:0]   s_ctrlport_resp_status,
53  output wire [31:0]  s_ctrlport_resp_data,
54  // Control Port Master (Request)
55  output wire         m_ctrlport_req_wr,
56  output wire         m_ctrlport_req_rd,
57  output wire [19:0]  m_ctrlport_req_addr,
58  output wire [31:0]  m_ctrlport_req_data,
59  output wire [3:0]   m_ctrlport_req_byte_en,
60  // Control Port Master (Response)
61  input  wire         m_ctrlport_resp_ack,
62  input  wire [1:0]   m_ctrlport_resp_status,
63  input  wire [31:0]  m_ctrlport_resp_data
64);
65
66  `include "../core/rfnoc_chdr_utils.vh"
67  `include "../core/rfnoc_axis_ctrl_utils.vh"
68
69  // Control triggers:
70  // - pending: A command is waiting on the input port
71  // - ontime: The timed command is due for execution (on time)
72  // - late: The timed command is late
73  // - exec: Execute the command (pass it to the output)
74  // - consume: Consume the input command
75  wire         pending, ontime, late, exec, consume;
76  // Cached values for input command
77  wire         cached_req_wr, cached_req_rd;
78  wire [19:0]  cached_req_addr;
79  wire [31:0]  cached_req_data;
80  wire [3:0]   cached_req_byte_en;
81  wire         cached_req_has_time;
82  wire [63:0]  cached_req_time;
83
84  axi_fifo_flop #(.WIDTH(1+1+20+32+4+1+64)) req_cache_i (
85    .clk(clk), .reset(rst), .clear(1'b0),
86    .i_tdata({s_ctrlport_req_wr, s_ctrlport_req_rd, s_ctrlport_req_addr, s_ctrlport_req_data,
87              s_ctrlport_req_byte_en, s_ctrlport_req_has_time, s_ctrlport_req_time}),
88    .i_tvalid(s_ctrlport_req_wr | s_ctrlport_req_rd), .i_tready(),
89    .o_tdata({cached_req_wr, cached_req_rd, cached_req_addr, cached_req_data,
90              cached_req_byte_en, cached_req_has_time, cached_req_time}),
91    .o_tvalid(pending), .o_tready(consume),
92    .occupied(), .space()
93  );
94
95  // Command is on time
96  assign ontime = cached_req_has_time && pending && time_now_stb &&
97    (cached_req_time[63:PRECISION_BITS] == time_now[63:PRECISION_BITS]);
98  // Command is late
99  assign late = cached_req_has_time && pending && time_now_stb &&
100    (cached_req_time[63:PRECISION_BITS] < time_now[63:PRECISION_BITS]);
101  // Logic to pass cmd forward
102  assign exec = pending && (!cached_req_has_time || ontime || (EXEC_LATE_CMDS && late));
103  assign consume = exec || late;
104
105  assign m_ctrlport_req_wr      = cached_req_wr & exec;
106  assign m_ctrlport_req_rd      = cached_req_rd & exec;
107  assign m_ctrlport_req_addr    = cached_req_addr;
108  assign m_ctrlport_req_data    = cached_req_data;
109  assign m_ctrlport_req_byte_en = cached_req_byte_en;
110
111  wire [1:0] resp_status = (late && !exec) ? AXIS_CTRL_STS_TSERR : m_ctrlport_resp_status;
112  axi_fifo_flop #(.WIDTH(2+32)) resp_cache_i (
113    .clk(clk), .reset(rst), .clear(1'b0),
114    .i_tdata({resp_status, m_ctrlport_resp_data}),
115    .i_tvalid(m_ctrlport_resp_ack || (late && !exec)), .i_tready(),
116    .o_tdata({s_ctrlport_resp_status, s_ctrlport_resp_data}),
117    .o_tvalid(s_ctrlport_resp_ack), .o_tready(s_ctrlport_resp_ack),
118    .occupied(), .space()
119  );
120
121endmodule // ctrlport_timer
122
123