1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00
corundum/fpga/common/rtl/mqnic_ptp_clock.v
Alex Forencich 448fa8eb4c Use SPDX
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-06-26 11:44:57 -07:00

351 lines
12 KiB
Verilog

// SPDX-License-Identifier: BSD-2-Clause-Views
/*
* Copyright (c) 2021-2023 The Regents of the University of California
*/
// Language: Verilog 2001
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* PTP hardware clock
*/
module mqnic_ptp_clock #
(
parameter PTP_CLK_PERIOD_NS_NUM = 4,
parameter PTP_CLK_PERIOD_NS_DENOM = 1,
parameter PTP_CLOCK_PIPELINE = 0,
parameter PTP_CLOCK_CDC_PIPELINE = 0,
parameter PTP_USE_SAMPLE_CLOCK = 0,
parameter PTP_PEROUT_ENABLE = 0,
parameter PTP_PEROUT_COUNT = 1,
parameter REG_ADDR_WIDTH = 7,
parameter REG_DATA_WIDTH = 32,
parameter REG_STRB_WIDTH = (REG_DATA_WIDTH/8),
parameter RB_BASE_ADDR = 0,
parameter RB_NEXT_PTR = 0
)
(
input wire clk,
input wire rst,
/*
* Register interface
*/
input wire [REG_ADDR_WIDTH-1:0] reg_wr_addr,
input wire [REG_DATA_WIDTH-1:0] reg_wr_data,
input wire [REG_STRB_WIDTH-1:0] reg_wr_strb,
input wire reg_wr_en,
output wire reg_wr_wait,
output wire reg_wr_ack,
input wire [REG_ADDR_WIDTH-1:0] reg_rd_addr,
input wire reg_rd_en,
output wire [REG_DATA_WIDTH-1:0] reg_rd_data,
output wire reg_rd_wait,
output wire reg_rd_ack,
/*
* PTP clock
*/
input wire ptp_clk,
input wire ptp_rst,
input wire ptp_sample_clk,
output wire ptp_pps,
output wire ptp_pps_str,
output wire [95:0] ptp_ts_96,
output wire ptp_ts_step,
output wire ptp_sync_pps,
output wire [95:0] ptp_sync_ts_96,
output wire ptp_sync_ts_step
);
parameter PTP_FNS_WIDTH = 32;
parameter PTP_CLK_PERIOD_NS = PTP_CLK_PERIOD_NS_NUM / PTP_CLK_PERIOD_NS_DENOM;
parameter PTP_CLK_PERIOD_NS_REM = PTP_CLK_PERIOD_NS_NUM - PTP_CLK_PERIOD_NS*PTP_CLK_PERIOD_NS_DENOM;
parameter PTP_CLK_PERIOD_FNS = (PTP_CLK_PERIOD_NS_REM * {32'd1, {PTP_FNS_WIDTH{1'b0}}}) / PTP_CLK_PERIOD_NS_DENOM;
parameter PTP_CLK_PERIOD_FNS_REM = (PTP_CLK_PERIOD_NS_REM * {32'd1, {PTP_FNS_WIDTH{1'b0}}}) - PTP_CLK_PERIOD_FNS*PTP_CLK_PERIOD_NS_DENOM;
parameter PTP_PERIOD_NS_WIDTH = $clog2(PTP_CLK_PERIOD_NS+1) + 2;
parameter PTP_OFFSET_NS_WIDTH = 30;
localparam RBB = RB_BASE_ADDR & {REG_ADDR_WIDTH{1'b1}};
// check configuration
initial begin
if (REG_DATA_WIDTH != 32) begin
$error("Error: Register interface width must be 32 (instance %m)");
$finish;
end
if (REG_STRB_WIDTH * 8 != REG_DATA_WIDTH) begin
$error("Error: Register interface requires byte (8-bit) granularity (instance %m)");
$finish;
end
if (REG_ADDR_WIDTH < 7) begin
$error("Error: Register address width too narrow (instance %m)");
$finish;
end
if (RB_NEXT_PTR >= RB_BASE_ADDR && RB_NEXT_PTR < RB_BASE_ADDR + 7'h60) begin
$error("Error: RB_NEXT_PTR overlaps block (instance %m)");
$finish;
end
end
// control registers
reg reg_wr_ack_reg = 1'b0;
reg [REG_DATA_WIDTH-1:0] reg_rd_data_reg = 0;
reg reg_rd_ack_reg = 1'b0;
reg [95:0] get_ptp_ts_96_reg = 0;
reg [95:0] set_ptp_ts_96_reg = 0;
reg set_ptp_ts_96_valid_reg = 0;
reg [PTP_PERIOD_NS_WIDTH-1:0] set_ptp_period_ns_reg = PTP_CLK_PERIOD_NS;
reg [PTP_FNS_WIDTH-1:0] set_ptp_period_fns_reg = PTP_CLK_PERIOD_FNS;
reg set_ptp_period_valid_reg = 0;
reg [PTP_OFFSET_NS_WIDTH-1:0] set_ptp_offset_ns_reg = 0;
reg [PTP_FNS_WIDTH-1:0] set_ptp_offset_fns_reg = 0;
reg [15:0] set_ptp_offset_count_reg = 0;
reg set_ptp_offset_valid_reg = 0;
wire set_ptp_offset_active;
assign reg_wr_wait = 1'b0;
assign reg_wr_ack = reg_wr_ack_reg;
assign reg_rd_data = reg_rd_data_reg;
assign reg_rd_wait = 1'b0;
assign reg_rd_ack = reg_rd_ack_reg;
always @(posedge clk) begin
reg_wr_ack_reg <= 1'b0;
reg_rd_data_reg <= 0;
reg_rd_ack_reg <= 1'b0;
if (reg_wr_en && !reg_wr_ack_reg) begin
// write operation
reg_wr_ack_reg <= 1'b1;
case ({reg_wr_addr >> 2, 2'b00})
// PHC
RBB+7'h30: set_ptp_ts_96_reg[15:0] <= reg_wr_data; // PTP set fns
RBB+7'h34: set_ptp_ts_96_reg[45:16] <= reg_wr_data; // PTP set ns
RBB+7'h38: set_ptp_ts_96_reg[79:48] <= reg_wr_data; // PTP set sec l
RBB+7'h3C: begin
// PTP set sec h
set_ptp_ts_96_reg[95:80] <= reg_wr_data;
set_ptp_ts_96_valid_reg <= !set_ptp_ts_96_valid_reg;
end
RBB+7'h40: set_ptp_period_fns_reg <= reg_wr_data; // PTP period fns
RBB+7'h44: begin
// PTP period ns
set_ptp_period_ns_reg <= reg_wr_data;
set_ptp_period_valid_reg <= !set_ptp_period_valid_reg;
end
RBB+7'h50: set_ptp_offset_fns_reg <= reg_wr_data; // PTP offset fns
RBB+7'h54: set_ptp_offset_ns_reg <= reg_wr_data; // PTP offset ns
RBB+7'h58: begin
// PTP offset count
set_ptp_offset_count_reg <= reg_wr_data;
set_ptp_offset_valid_reg <= !set_ptp_offset_valid_reg;
end
default: reg_wr_ack_reg <= 1'b0;
endcase
end
if (reg_rd_en && !reg_rd_ack_reg) begin
// read operation
reg_rd_ack_reg <= 1'b1;
case ({reg_rd_addr >> 2, 2'b00})
// PHC
RBB+7'h00: reg_rd_data_reg <= 32'h0000C080; // PHC: Type
RBB+7'h04: reg_rd_data_reg <= 32'h00000100; // PHC: Version
RBB+7'h08: reg_rd_data_reg <= RB_NEXT_PTR; // PHC: Next header
RBB+7'h0C: begin
// PHC features
reg_rd_data_reg[7:0] <= PTP_PEROUT_ENABLE ? PTP_PEROUT_COUNT : 0;
reg_rd_data_reg[15:8] <= 0;
reg_rd_data_reg[23:16] <= 0;
reg_rd_data_reg[31:24] <= 0;
end
RBB+7'h10: reg_rd_data_reg <= ptp_sync_ts_96[15:0]; // PTP cur fns
RBB+7'h14: reg_rd_data_reg <= ptp_sync_ts_96[45:16]; // PTP cur ns
RBB+7'h18: reg_rd_data_reg <= ptp_sync_ts_96[79:48]; // PTP cur sec l
RBB+7'h1C: reg_rd_data_reg <= ptp_sync_ts_96[95:80]; // PTP cur sec h
RBB+7'h20: begin
// PTP get fns
get_ptp_ts_96_reg <= ptp_sync_ts_96;
reg_rd_data_reg <= ptp_sync_ts_96[15:0];
end
RBB+7'h24: reg_rd_data_reg <= get_ptp_ts_96_reg[45:16]; // PTP get ns
RBB+7'h28: reg_rd_data_reg <= get_ptp_ts_96_reg[79:48]; // PTP get sec l
RBB+7'h2C: reg_rd_data_reg <= get_ptp_ts_96_reg[95:80]; // PTP get sec h
RBB+7'h30: reg_rd_data_reg <= set_ptp_ts_96_reg[15:0]; // PTP set fns
RBB+7'h34: reg_rd_data_reg <= set_ptp_ts_96_reg[45:16]; // PTP set ns
RBB+7'h38: reg_rd_data_reg <= set_ptp_ts_96_reg[79:48]; // PTP set sec l
RBB+7'h3C: reg_rd_data_reg <= set_ptp_ts_96_reg[95:80]; // PTP set sec h
RBB+7'h40: reg_rd_data_reg <= set_ptp_period_fns_reg; // PTP period fns
RBB+7'h44: reg_rd_data_reg <= set_ptp_period_ns_reg; // PTP period ns
RBB+7'h48: reg_rd_data_reg <= PTP_CLK_PERIOD_FNS; // PTP nom period fns
RBB+7'h4C: reg_rd_data_reg <= PTP_CLK_PERIOD_NS; // PTP nom period ns
RBB+7'h50: reg_rd_data_reg <= set_ptp_offset_fns_reg; // PTP offset fns
RBB+7'h54: reg_rd_data_reg <= set_ptp_offset_ns_reg; // PTP offset ns
RBB+7'h58: reg_rd_data_reg <= set_ptp_offset_count_reg; // PTP offset count
RBB+7'h5C: reg_rd_data_reg <= set_ptp_offset_active; // PTP offset status
default: reg_rd_ack_reg <= 1'b0;
endcase
end
if (rst) begin
reg_wr_ack_reg <= 1'b0;
reg_rd_ack_reg <= 1'b0;
set_ptp_period_ns_reg <= PTP_CLK_PERIOD_NS;
set_ptp_period_fns_reg <= PTP_CLK_PERIOD_FNS;
end
end
(* shreg_extract = "no" *)
reg set_ptp_ts_96_valid_sync_1_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_ts_96_valid_sync_2_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_ts_96_valid_sync_3_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_period_valid_sync_1_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_period_valid_sync_2_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_period_valid_sync_3_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_offset_valid_sync_1_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_offset_valid_sync_2_reg = 1'b0;
(* shreg_extract = "no" *)
reg set_ptp_offset_valid_sync_3_reg = 1'b0;
always @(posedge ptp_clk) begin
set_ptp_ts_96_valid_sync_1_reg <= set_ptp_ts_96_valid_reg;
set_ptp_ts_96_valid_sync_2_reg <= set_ptp_ts_96_valid_sync_1_reg;
set_ptp_ts_96_valid_sync_3_reg <= set_ptp_ts_96_valid_sync_2_reg;
set_ptp_period_valid_sync_1_reg <= set_ptp_period_valid_reg;
set_ptp_period_valid_sync_2_reg <= set_ptp_period_valid_sync_1_reg;
set_ptp_period_valid_sync_3_reg <= set_ptp_period_valid_sync_2_reg;
set_ptp_offset_valid_sync_1_reg <= set_ptp_offset_valid_reg;
set_ptp_offset_valid_sync_2_reg <= set_ptp_offset_valid_sync_1_reg;
set_ptp_offset_valid_sync_3_reg <= set_ptp_offset_valid_sync_2_reg;
end
// PTP clock
ptp_clock #(
.PERIOD_NS_WIDTH(PTP_PERIOD_NS_WIDTH),
.OFFSET_NS_WIDTH(PTP_OFFSET_NS_WIDTH),
.FNS_WIDTH(PTP_FNS_WIDTH),
.PERIOD_NS(PTP_CLK_PERIOD_NS),
.PERIOD_FNS(PTP_CLK_PERIOD_FNS),
.DRIFT_ENABLE(0),
.DRIFT_NS(0),
.DRIFT_FNS(PTP_CLK_PERIOD_FNS_REM),
.DRIFT_RATE(PTP_CLK_PERIOD_NS_DENOM),
.PIPELINE_OUTPUT(PTP_CLOCK_PIPELINE)
)
ptp_clock_inst (
.clk(ptp_clk),
.rst(ptp_rst),
/*
* Timestamp inputs for synchronization
*/
.input_ts_96(set_ptp_ts_96_reg),
.input_ts_96_valid(set_ptp_ts_96_valid_sync_2_reg ^ set_ptp_ts_96_valid_sync_3_reg),
.input_ts_64(0),
.input_ts_64_valid(1'b0),
/*
* Period adjustment
*/
.input_period_ns(set_ptp_period_ns_reg),
.input_period_fns(set_ptp_period_fns_reg),
.input_period_valid(set_ptp_period_valid_sync_2_reg ^ set_ptp_period_valid_sync_3_reg),
/*
* Offset adjustment
*/
.input_adj_ns(set_ptp_offset_ns_reg),
.input_adj_fns(set_ptp_offset_fns_reg),
.input_adj_count(set_ptp_offset_count_reg),
.input_adj_valid(set_ptp_offset_valid_sync_2_reg ^ set_ptp_offset_valid_sync_3_reg),
// .input_adj_active(set_ptp_offset_active),
/*
* Drift adjustment
*/
.input_drift_ns(0),
.input_drift_fns(0),
.input_drift_rate(0),
.input_drift_valid(0),
/*
* Timestamp outputs
*/
.output_ts_96(ptp_ts_96),
.output_ts_64(),
.output_ts_step(ptp_ts_step),
/*
* PPS output
*/
.output_pps(ptp_pps)
);
// stretched PPS output
localparam PPS_CNT_PERIOD = (64'd500_000_000*PTP_CLK_PERIOD_NS_DENOM)/PTP_CLK_PERIOD_NS_NUM;
reg [$clog2(PPS_CNT_PERIOD)-1:0] pps_counter_reg = 0;
reg pps_str_reg = 0;
always @(posedge ptp_clk) begin
pps_str_reg <= 1'b0;
if (ptp_pps) begin
pps_counter_reg <= PPS_CNT_PERIOD;
pps_str_reg <= 1'b1;
end else if (pps_counter_reg > 0) begin
pps_counter_reg <= pps_counter_reg - 1;
pps_str_reg <= 1'b1;
end
end
assign ptp_pps_str = pps_str_reg;
// sync to core clock domain
ptp_clock_cdc #(
.TS_WIDTH(96),
.NS_WIDTH(PTP_PERIOD_NS_WIDTH),
.FNS_WIDTH(16),
.USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK),
.PIPELINE_OUTPUT(PTP_CLOCK_CDC_PIPELINE)
)
ptp_cdc_inst (
.input_clk(ptp_clk),
.input_rst(ptp_rst),
.output_clk(clk),
.output_rst(rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts_96),
.input_ts_step(ptp_ts_step),
.output_ts(ptp_sync_ts_96),
.output_ts_step(ptp_sync_ts_step),
.output_pps(ptp_sync_pps),
.locked()
);
endmodule
`resetall