mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
835f0d38f0
Signed-off-by: Alex Forencich <alex@alexforencich.com>
358 lines
13 KiB
Verilog
358 lines
13 KiB
Verilog
/*
|
|
|
|
Copyright 2021, The Regents of the University of California.
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS
|
|
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
OF SUCH DAMAGE.
|
|
|
|
The views and conclusions contained in the software and documentation are those
|
|
of the authors and should not be interpreted as representing official policies,
|
|
either expressed or implied, of 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 [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 = 32;
|
|
|
|
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)
|
|
);
|
|
|
|
// 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
|