mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
448fa8eb4c
Signed-off-by: Alex Forencich <alex@alexforencich.com>
376 lines
13 KiB
Verilog
376 lines
13 KiB
Verilog
// SPDX-License-Identifier: BSD-2-Clause-Views
|
|
/*
|
|
* Copyright (c) 2019-2023 The Regents of the University of California
|
|
*/
|
|
|
|
// Language: Verilog 2001
|
|
|
|
`resetall
|
|
`timescale 1ns / 1ps
|
|
`default_nettype none
|
|
|
|
/*
|
|
* TDMA scheduler module
|
|
*/
|
|
module tdma_scheduler #
|
|
(
|
|
// Timeslot index width
|
|
parameter INDEX_WIDTH = 8,
|
|
// Schedule absolute PTP start time, seconds part
|
|
parameter SCHEDULE_START_S = 48'h0,
|
|
// Schedule absolute PTP start time, nanoseconds part
|
|
parameter SCHEDULE_START_NS = 30'h0,
|
|
// Schedule period, seconds part
|
|
parameter SCHEDULE_PERIOD_S = 48'd0,
|
|
// Schedule period, nanoseconds part
|
|
parameter SCHEDULE_PERIOD_NS = 30'd1000000,
|
|
// Timeslot period, seconds part
|
|
parameter TIMESLOT_PERIOD_S = 48'd0,
|
|
// Timeslot period, nanoseconds part
|
|
parameter TIMESLOT_PERIOD_NS = 30'd100000,
|
|
// Timeslot active period, seconds part
|
|
parameter ACTIVE_PERIOD_S = 48'd0,
|
|
// Timeslot active period, nanoseconds part
|
|
parameter ACTIVE_PERIOD_NS = 30'd100000
|
|
)
|
|
(
|
|
input wire clk,
|
|
input wire rst,
|
|
|
|
/*
|
|
* Timestamp input from PTP clock
|
|
*/
|
|
input wire [95:0] input_ts_96,
|
|
input wire input_ts_step,
|
|
|
|
/*
|
|
* Control
|
|
*/
|
|
input wire enable,
|
|
input wire [79:0] input_schedule_start,
|
|
input wire input_schedule_start_valid,
|
|
input wire [79:0] input_schedule_period,
|
|
input wire input_schedule_period_valid,
|
|
input wire [79:0] input_timeslot_period,
|
|
input wire input_timeslot_period_valid,
|
|
input wire [79:0] input_active_period,
|
|
input wire input_active_period_valid,
|
|
|
|
/*
|
|
* Status
|
|
*/
|
|
output wire locked,
|
|
output wire error,
|
|
|
|
/*
|
|
* TDMA schedule outputs
|
|
*/
|
|
output wire schedule_start,
|
|
output wire [INDEX_WIDTH-1:0] timeslot_index,
|
|
output wire timeslot_start,
|
|
output wire timeslot_end,
|
|
output wire timeslot_active
|
|
);
|
|
|
|
/*
|
|
|
|
schedule
|
|
start
|
|
|
|
|
V
|
|
|<-------- schedule period -------->|
|
|
-----+--------+--------+--------+--------+--------+---
|
|
| SLOT 0 | SLOT 1 | SLOT 2 | SLOT 3 | SLOT 0 |
|
|
-----+--------+--------+--------+--------+--------+---
|
|
|<------>|
|
|
timeslot
|
|
period
|
|
|
|
|
|
|<-------- timeslot period -------->|
|
|
-----+-----------------------------------+------------
|
|
| SLOT 0 | SLOT 1
|
|
-----+-----------------------------------+------------
|
|
|<---- active period ----->|
|
|
|
|
*/
|
|
|
|
localparam [2:0]
|
|
STATE_IDLE = 3'd0,
|
|
STATE_UPDATE_SCHEDULE_1 = 3'd1,
|
|
STATE_UPDATE_SCHEDULE_2 = 3'd2,
|
|
STATE_UPDATE_SLOT_1 = 3'd3,
|
|
STATE_UPDATE_SLOT_2 = 3'd4,
|
|
STATE_UPDATE_SLOT_3 = 3'd5,
|
|
STATE_WAIT = 3'd6;
|
|
|
|
reg [2:0] state_reg = STATE_IDLE, state_next;
|
|
|
|
reg [47:0] time_s_reg = 0;
|
|
reg [30:0] time_ns_reg = 0;
|
|
|
|
reg [47:0] first_slot_s_reg = 0, first_slot_s_next;
|
|
reg [30:0] first_slot_ns_reg = 0, first_slot_ns_next;
|
|
|
|
reg [47:0] next_slot_s_reg = 0, next_slot_s_next;
|
|
reg [30:0] next_slot_ns_reg = 0, next_slot_ns_next;
|
|
|
|
reg [47:0] active_end_s_reg = 0, active_end_s_next;
|
|
reg [30:0] active_end_ns_reg = 0, active_end_ns_next;
|
|
|
|
reg [47:0] schedule_start_s_reg = SCHEDULE_START_S;
|
|
reg [30:0] schedule_start_ns_reg = SCHEDULE_START_NS;
|
|
|
|
reg [47:0] schedule_period_s_reg = SCHEDULE_PERIOD_S;
|
|
reg [30:0] schedule_period_ns_reg = SCHEDULE_PERIOD_NS;
|
|
|
|
reg [47:0] timeslot_period_s_reg = TIMESLOT_PERIOD_S;
|
|
reg [30:0] timeslot_period_ns_reg = TIMESLOT_PERIOD_NS;
|
|
|
|
reg [47:0] active_period_s_reg = ACTIVE_PERIOD_S;
|
|
reg [30:0] active_period_ns_reg = ACTIVE_PERIOD_NS;
|
|
|
|
reg [29:0] ts_ns_inc_reg = 0, ts_ns_inc_next;
|
|
reg [30:0] ts_ns_ovf_reg = 0, ts_ns_ovf_next;
|
|
|
|
reg locked_reg = 1'b0, locked_next;
|
|
reg locked_int_reg = 1'b0, locked_int_next;
|
|
reg error_reg = 1'b0, error_next;
|
|
reg schedule_running_reg = 1'b0, schedule_running_next;
|
|
|
|
reg schedule_start_reg = 1'b0, schedule_start_next;
|
|
reg [INDEX_WIDTH-1:0] timeslot_index_reg = 0, timeslot_index_next;
|
|
reg timeslot_start_reg = 1'b0, timeslot_start_next;
|
|
reg timeslot_end_reg = 1'b0, timeslot_end_next;
|
|
reg timeslot_active_reg = 1'b0, timeslot_active_next;
|
|
|
|
assign locked = locked_reg;
|
|
assign error = error_reg;
|
|
|
|
assign schedule_start = schedule_start_reg;
|
|
assign timeslot_index = timeslot_index_reg;
|
|
assign timeslot_start = timeslot_start_reg;
|
|
assign timeslot_end = timeslot_end_reg;
|
|
assign timeslot_active = timeslot_active_reg;
|
|
|
|
always @* begin
|
|
state_next = STATE_IDLE;
|
|
|
|
first_slot_s_next = first_slot_s_reg;
|
|
first_slot_ns_next = first_slot_ns_reg;
|
|
|
|
next_slot_s_next = next_slot_s_reg;
|
|
next_slot_ns_next = next_slot_ns_reg;
|
|
|
|
active_end_s_next = active_end_s_reg;
|
|
active_end_ns_next = active_end_ns_reg;
|
|
|
|
ts_ns_inc_next = ts_ns_inc_reg;
|
|
|
|
ts_ns_ovf_next = ts_ns_ovf_reg;
|
|
|
|
locked_next = locked_reg;
|
|
locked_int_next = locked_int_reg;
|
|
error_next = error_reg;
|
|
schedule_running_next = schedule_running_reg;
|
|
|
|
schedule_start_next = 1'b0;
|
|
timeslot_index_next = timeslot_index_reg;
|
|
timeslot_start_next = 1'b0;
|
|
timeslot_end_next = 1'b0;
|
|
timeslot_active_next = timeslot_active_reg;
|
|
|
|
if (input_schedule_start_valid || input_schedule_period_valid || input_ts_step) begin
|
|
timeslot_index_next = 0;
|
|
timeslot_start_next = 1'b0;
|
|
timeslot_end_next = timeslot_active_reg;
|
|
timeslot_active_next = 1'b0;
|
|
error_next = input_ts_step;
|
|
state_next = STATE_IDLE;
|
|
end else begin
|
|
case (state_reg)
|
|
STATE_IDLE: begin
|
|
// set next rise to start time
|
|
first_slot_s_next = schedule_start_s_reg;
|
|
first_slot_ns_next = schedule_start_ns_reg;
|
|
next_slot_s_next = schedule_start_s_reg;
|
|
next_slot_ns_next = schedule_start_ns_reg;
|
|
timeslot_index_next = 0;
|
|
timeslot_start_next = 1'b0;
|
|
timeslot_end_next = timeslot_active_reg;
|
|
timeslot_active_next = 1'b0;
|
|
locked_next = 1'b0;
|
|
locked_int_next = 1'b0;
|
|
schedule_running_next = 1'b0;
|
|
state_next = STATE_WAIT;
|
|
end
|
|
STATE_UPDATE_SCHEDULE_1: begin
|
|
// set next schedule start time to next schedule start time plus schedule period
|
|
ts_ns_inc_next = first_slot_ns_reg + schedule_period_ns_reg;
|
|
ts_ns_ovf_next = first_slot_ns_reg + schedule_period_ns_reg - 31'd1_000_000_000;
|
|
state_next = STATE_UPDATE_SCHEDULE_2;
|
|
end
|
|
STATE_UPDATE_SCHEDULE_2: begin
|
|
if (!ts_ns_ovf_reg[30]) begin
|
|
// if the overflow lookahead did not borrow, one second has elapsed
|
|
first_slot_s_next = first_slot_s_reg + schedule_period_s_reg + 1;
|
|
first_slot_ns_next = ts_ns_ovf_reg;
|
|
end else begin
|
|
// no increment seconds field
|
|
first_slot_s_next = first_slot_s_reg + schedule_period_s_reg;
|
|
first_slot_ns_next = ts_ns_inc_reg;
|
|
end
|
|
next_slot_s_next = first_slot_s_reg;
|
|
next_slot_ns_next = first_slot_ns_reg;
|
|
state_next = STATE_UPDATE_SLOT_1;
|
|
end
|
|
STATE_UPDATE_SLOT_1: begin
|
|
// set next fall time to next rise time plus width
|
|
ts_ns_inc_next = next_slot_ns_reg + active_period_ns_reg;
|
|
ts_ns_ovf_next = next_slot_ns_reg + active_period_ns_reg - 31'd1_000_000_000;
|
|
state_next = STATE_UPDATE_SLOT_2;
|
|
end
|
|
STATE_UPDATE_SLOT_2: begin
|
|
if (!ts_ns_ovf_reg[30]) begin
|
|
// if the overflow lookahead did not borrow, one second has elapsed
|
|
active_end_s_next = next_slot_s_reg + active_period_s_reg + 1;
|
|
active_end_ns_next = ts_ns_ovf_reg;
|
|
end else begin
|
|
// no increment seconds field
|
|
active_end_s_next = next_slot_s_reg + active_period_s_reg;
|
|
active_end_ns_next = ts_ns_inc_reg;
|
|
end
|
|
// set next timeslot start time to next timeslot start time plus timeslot period
|
|
ts_ns_inc_next = next_slot_ns_reg + timeslot_period_ns_reg;
|
|
ts_ns_ovf_next = next_slot_ns_reg + timeslot_period_ns_reg - 31'd1_000_000_000;
|
|
state_next = STATE_UPDATE_SLOT_3;
|
|
end
|
|
STATE_UPDATE_SLOT_3: begin
|
|
if (!ts_ns_ovf_reg[30]) begin
|
|
// if the overflow lookahead did not borrow, one second has elapsed
|
|
next_slot_s_next = next_slot_s_reg + timeslot_period_s_reg + 1;
|
|
next_slot_ns_next = ts_ns_ovf_reg;
|
|
end else begin
|
|
// no increment seconds field
|
|
next_slot_s_next = next_slot_s_reg + timeslot_period_s_reg;
|
|
next_slot_ns_next = ts_ns_inc_reg;
|
|
end
|
|
state_next = STATE_WAIT;
|
|
end
|
|
STATE_WAIT: begin
|
|
if ((time_s_reg > first_slot_s_reg) || (time_s_reg == first_slot_s_reg && time_ns_reg > first_slot_ns_reg)) begin
|
|
// start of next schedule period
|
|
schedule_start_next = enable && locked_int_reg;
|
|
timeslot_index_next = 0;
|
|
timeslot_start_next = enable && locked_int_reg;
|
|
timeslot_end_next = timeslot_active_reg;
|
|
timeslot_active_next = enable && locked_int_reg;
|
|
schedule_running_next = 1'b1;
|
|
locked_next = locked_int_reg;
|
|
error_next = error_reg && !locked_int_reg;
|
|
state_next = STATE_UPDATE_SCHEDULE_1;
|
|
end else if ((time_s_reg > next_slot_s_reg) || (time_s_reg == next_slot_s_reg && time_ns_reg > next_slot_ns_reg)) begin
|
|
// start of next timeslot
|
|
timeslot_index_next = timeslot_index_reg + 1;
|
|
timeslot_start_next = enable && locked_reg;
|
|
timeslot_end_next = timeslot_active_reg;
|
|
timeslot_active_next = enable && locked_reg;
|
|
state_next = STATE_UPDATE_SLOT_1;
|
|
end else if (timeslot_active_reg && ((time_s_reg > active_end_s_reg) || (time_s_reg == active_end_s_reg && time_ns_reg > active_end_ns_reg))) begin
|
|
// end of timeslot
|
|
timeslot_end_next = 1'b1;
|
|
timeslot_active_next = 1'b0;
|
|
state_next = STATE_WAIT;
|
|
end else begin
|
|
locked_int_next = schedule_running_reg;
|
|
state_next = STATE_WAIT;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
always @(posedge clk) begin
|
|
state_reg <= state_next;
|
|
|
|
time_s_reg <= input_ts_96[95:48];
|
|
time_ns_reg <= input_ts_96[45:16];
|
|
|
|
if (input_schedule_start_valid) begin
|
|
schedule_start_s_reg <= input_schedule_start[79:32];
|
|
schedule_start_ns_reg <= input_schedule_start[31:0];
|
|
end
|
|
|
|
if (input_schedule_period_valid) begin
|
|
schedule_period_s_reg <= input_schedule_period[79:32];
|
|
schedule_period_ns_reg <= input_schedule_period[31:0];
|
|
end
|
|
|
|
if (input_timeslot_period_valid) begin
|
|
timeslot_period_s_reg <= input_timeslot_period[79:32];
|
|
timeslot_period_ns_reg <= input_timeslot_period[31:0];
|
|
end
|
|
|
|
if (input_active_period_valid) begin
|
|
active_period_s_reg <= input_active_period[79:32];
|
|
active_period_ns_reg <= input_active_period[31:0];
|
|
end
|
|
|
|
first_slot_s_reg <= first_slot_s_next;
|
|
first_slot_ns_reg <= first_slot_ns_next;
|
|
|
|
next_slot_s_reg <= next_slot_s_next;
|
|
next_slot_ns_reg <= next_slot_ns_next;
|
|
|
|
active_end_s_reg <= active_end_s_next;
|
|
active_end_ns_reg <= active_end_ns_next;
|
|
|
|
ts_ns_inc_reg <= ts_ns_inc_next;
|
|
ts_ns_ovf_reg <= ts_ns_ovf_next;
|
|
|
|
locked_reg <= locked_next;
|
|
locked_int_reg <= locked_int_next;
|
|
error_reg <= error_next;
|
|
schedule_running_reg <= schedule_running_next;
|
|
|
|
schedule_start_reg <= schedule_start_next;
|
|
timeslot_index_reg <= timeslot_index_next;
|
|
timeslot_start_reg <= timeslot_start_next;
|
|
timeslot_end_reg <= timeslot_end_next;
|
|
timeslot_active_reg <= timeslot_active_next;
|
|
|
|
if (rst) begin
|
|
state_reg <= STATE_IDLE;
|
|
|
|
time_s_reg <= 0;
|
|
time_ns_reg <= 0;
|
|
|
|
schedule_start_s_reg <= SCHEDULE_START_S;
|
|
schedule_start_ns_reg <= SCHEDULE_START_NS;
|
|
|
|
schedule_period_s_reg <= SCHEDULE_PERIOD_S;
|
|
schedule_period_ns_reg <= SCHEDULE_PERIOD_NS;
|
|
|
|
timeslot_period_s_reg <= TIMESLOT_PERIOD_S;
|
|
timeslot_period_ns_reg <= TIMESLOT_PERIOD_NS;
|
|
|
|
active_period_s_reg <= ACTIVE_PERIOD_S;
|
|
active_period_ns_reg <= ACTIVE_PERIOD_NS;
|
|
|
|
locked_reg <= 1'b0;
|
|
locked_int_reg <= 1'b0;
|
|
error_reg <= 1'b0;
|
|
schedule_running_reg <= 1'b0;
|
|
|
|
schedule_start_reg <= 1'b0;
|
|
timeslot_index_reg <= 0;
|
|
timeslot_start_reg <= 1'b0;
|
|
timeslot_end_reg <= 1'b0;
|
|
timeslot_active_reg <= 1'b0;
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
`resetall
|