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
171
172
173
174
175
176
177
178
179
|
//
// Copyright 2021 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: ctrlport_timer
//
// Description:
// The Control-Port timer module converts an asynchronous timed
// transaction into a synchronous blocking transaction. This
// module will use the input req_has_time and req_time fields and
// produce an output transaction that will execute when the requested
// time is current. The module does not pass the has_time and time
// signals out because they are no longer relevant. The current time
// is an input to this module, and must be a monotonic counter that
// updates every time the time strobe is asserted.
//
// Parameters:
// - EXEC_LATE_CMDS : If EXEC_LATE_CMDS = 0 and a command is late, then a
// TSERR response is returned. If EXEC_LATE_CMDS = 1, then
// a late command will be passed to the output as if it
// were on time.
//
// Signals:
// - time_now* : The time_now signal is the current time and the stb
// signal indicates that the time_now is valid.
// - time_ignore_bits : Number of least-significant bits to ignore when doing
// time comparisons. This should be a constant.
// - s_ctrlport_* : The slave Control-Port bus.
// This must have the has_time and time signals.
// - m_ctrlport_* : The master Control-Port bus.
// This will not have the has_time and time signals.
module ctrlport_timer #(
parameter [0:0] EXEC_LATE_CMDS = 1
)(
// Clocks and Resets
input wire clk,
input wire rst,
// Timestamp (synchronous to clk)
input wire [63:0] time_now,
input wire time_now_stb,
input wire [3:0] time_ignore_bits,
// Control Port Master (Request)
input wire s_ctrlport_req_wr,
input wire s_ctrlport_req_rd,
input wire [19:0] s_ctrlport_req_addr,
input wire [31:0] s_ctrlport_req_data,
input wire [3:0] s_ctrlport_req_byte_en,
input wire s_ctrlport_req_has_time,
input wire [63:0] s_ctrlport_req_time,
// Control Port Slave (Response)
output wire s_ctrlport_resp_ack,
output wire [1:0] s_ctrlport_resp_status,
output wire [31:0] s_ctrlport_resp_data,
// Control Port Master (Request)
output wire m_ctrlport_req_wr,
output wire m_ctrlport_req_rd,
output wire [19:0] m_ctrlport_req_addr,
output wire [31:0] m_ctrlport_req_data,
output wire [3:0] m_ctrlport_req_byte_en,
// Control Port Master (Response)
input wire m_ctrlport_resp_ack,
input wire [1:0] m_ctrlport_resp_status,
input wire [31:0] m_ctrlport_resp_data
);
`include "../core/rfnoc_chdr_utils.vh"
`include "../core/rfnoc_axis_ctrl_utils.vh"
// Control triggers:
// - pending: A command is waiting on the input port
// - has_time: The command is timed
// - on_time: The timed command is due for execution (on time)
// - late: The timed command is late
// - exec: Execute the command (pass it to the output)
// - consume: Consume the input command
reg pending, has_time, on_time, late;
wire exec, consume, valid;
// Cached values for input command
wire cached_req_wr, cached_req_rd;
wire [19:0] cached_req_addr;
wire [31:0] cached_req_data;
wire [3:0] cached_req_byte_en;
wire cached_req_has_time;
wire [63:0] cached_req_time;
wire cached_req_valid;
axi_fifo_flop #(.WIDTH(1+1+20+32+4+1+64)) req_cache_i (
.clk(clk), .reset(rst), .clear(1'b0),
.i_tdata({s_ctrlport_req_wr, s_ctrlport_req_rd, s_ctrlport_req_addr, s_ctrlport_req_data,
s_ctrlport_req_byte_en, s_ctrlport_req_has_time, s_ctrlport_req_time}),
.i_tvalid(s_ctrlport_req_wr | s_ctrlport_req_rd), .i_tready(),
.o_tdata({cached_req_wr, cached_req_rd, cached_req_addr, cached_req_data,
cached_req_byte_en, cached_req_has_time, cached_req_time}),
.o_tvalid(cached_req_valid), .o_tready(consume),
.occupied(), .space()
);
// Create a mask so that we can ignore the lower time_ignore_bits of the time.
wire [63:0] mask = (~64'd0 << time_ignore_bits);
// Pipeline registers
reg has_time_0;
reg has_time_1;
reg valid_0;
reg valid_1;
reg [63:0] time_0;
reg time_stb_1, time_stb_0;
reg [63:0] req_time_0;
reg on_time_1;
reg late_1;
// This always block pipelines the time comparisons and other condition
// checks, so we don't have a long combinational path here. This adds some
// delay to the execution of timed commands, but this block is not intended
// for cycle-accurate register updates.
always @(posedge clk) begin
if (rst || consume) begin
has_time_0 <= 'bX;
time_stb_0 <= 'bX;
time_0 <= 'bX;
req_time_0 <= 'bX;
has_time_1 <= 'bX;
time_stb_1 <= 'bX;
on_time_1 <= 'bX;
late_1 <= 'bX;
has_time <= 'bX;
on_time <= 'bX;
// These need to be zeroed, to clear the pipeline:
valid_0 <= 1'b0;
valid_1 <= 1'b0;
pending <= 1'b0;
late <= 1'b0;
end else begin
// Stage 0
valid_0 <= cached_req_valid;
has_time_0 <= cached_req_has_time;
time_stb_0 <= time_now_stb;
time_0 <= time_now & mask;
req_time_0 <= cached_req_time & mask;
// Stage 1
valid_1 <= valid_0;
has_time_1 <= has_time_0;
time_stb_1 <= time_stb_0;
on_time_1 <= req_time_0 == time_0;
late_1 <= req_time_0 < time_0;
// Stage 3
pending <= valid_1;
has_time <= has_time_1;
on_time <= valid_1 && has_time_1 && time_stb_1 && on_time_1;
late <= valid_1 && has_time_1 && time_stb_1 && late_1;
end
end
// Logic to pass cmd forward
assign exec = pending && (!has_time || on_time || (EXEC_LATE_CMDS && late));
assign consume = exec || late;
assign m_ctrlport_req_wr = cached_req_wr & exec;
assign m_ctrlport_req_rd = cached_req_rd & exec;
assign m_ctrlport_req_addr = cached_req_addr;
assign m_ctrlport_req_data = cached_req_data;
assign m_ctrlport_req_byte_en = cached_req_byte_en;
wire [1:0] resp_status = (late && !exec) ? AXIS_CTRL_STS_TSERR : m_ctrlport_resp_status;
axi_fifo_flop #(.WIDTH(2+32)) resp_cache_i (
.clk(clk), .reset(rst), .clear(1'b0),
.i_tdata({resp_status, m_ctrlport_resp_data}),
.i_tvalid(m_ctrlport_resp_ack || (late && !exec)), .i_tready(),
.o_tdata({s_ctrlport_resp_status, s_ctrlport_resp_data}),
.o_tvalid(s_ctrlport_resp_ack), .o_tready(s_ctrlport_resp_ack),
.occupied(), .space()
);
endmodule // ctrlport_timer
|