This pipelines ctrlport_timer to eliminate the long combinational path caused by the time comparisons. This change also removes the PRECISION_BITS parameter and converts it to a signal named time_ignore_bits. Original-commit: c6035320215f270d7658cfb6195a96a0b84d6aa7
180 lines
6.6 KiB
Verilog
180 lines
6.6 KiB
Verilog
//
|
|
// 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
|
|
|