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
|