ptp: Add PTP TD PHC module and testbench

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-02-13 17:50:16 -08:00
parent 38a150b87a
commit 2eaa2f64a2
5 changed files with 1832 additions and 0 deletions

645
rtl/ptp/taxi_ptp_td_phc.sv Normal file
View File

@@ -0,0 +1,645 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2023-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* PTP time distribution PHC
*/
module taxi_ptp_td_phc #
(
parameter PERIOD_NS_NUM = 32,
parameter PERIOD_NS_DENOM = 5
)
(
input wire logic clk,
input wire logic rst,
/*
* ToD timestamp control
*/
input wire logic [47:0] input_ts_tod_s,
input wire logic [29:0] input_ts_tod_ns,
input wire logic input_ts_tod_valid,
output wire logic input_ts_tod_ready,
input wire logic [29:0] input_ts_tod_offset_ns,
input wire logic input_ts_tod_offset_valid,
output wire logic input_ts_tod_offset_ready,
/*
* Relative timestamp control
*/
input wire logic [47:0] input_ts_rel_ns,
input wire logic input_ts_rel_valid,
output wire logic input_ts_rel_ready,
input wire logic [31:0] input_ts_rel_offset_ns,
input wire logic input_ts_rel_offset_valid,
output wire logic input_ts_rel_offset_ready,
/*
* Fractional ns control
*/
input wire logic [31:0] input_ts_offset_fns,
input wire logic input_ts_offset_valid,
output wire logic input_ts_offset_ready,
/*
* Period control
*/
input wire logic [7:0] input_period_ns,
input wire logic [31:0] input_period_fns,
input wire logic input_period_valid,
output wire logic input_period_ready,
input wire logic [15:0] input_drift_num,
input wire logic [15:0] input_drift_denom,
input wire logic input_drift_valid,
output wire logic input_drift_ready,
/*
* Time distribution serial data output
*/
output wire logic ptp_td_sdo,
/*
* PPS output
*/
output wire logic output_pps,
output wire logic output_pps_str
);
localparam INC_NS_W = 9+8;
localparam PERIOD_NS_W = 8;
localparam TS_REL_NS_W = 48;
localparam TS_TOD_S_W = 48;
localparam TS_TOD_NS_W = 30;
localparam FNS_W = 32;
localparam PERIOD_NS = PERIOD_NS_NUM / PERIOD_NS_DENOM;
localparam PERIOD_NS_REM = PERIOD_NS_NUM - PERIOD_NS*PERIOD_NS_DENOM;
localparam PERIOD_FNS = (PERIOD_NS_REM * {32'd1, {FNS_W{1'b0}}}) / (32+FNS_W)'(PERIOD_NS_DENOM);
localparam PERIOD_FNS_REM = (PERIOD_NS_REM * {32'd1, {FNS_W{1'b0}}}) - PERIOD_FNS*PERIOD_NS_DENOM;
localparam [30:0] NS_PER_S = 31'd1_000_000_000;
logic [PERIOD_NS_W-1:0] period_ns_reg = PERIOD_NS_W'(PERIOD_NS);
logic [FNS_W-1:0] period_fns_reg = FNS_W'(PERIOD_FNS);
logic [15:0] drift_num_reg = 16'(PERIOD_FNS_REM);
logic [15:0] drift_denom_reg = 16'(PERIOD_NS_DENOM);
logic [15:0] drift_cnt_reg = '0;
logic [15:0] drift_cnt_d1_reg = '0;
logic drift_apply_reg = 1'b0;
logic [23:0] drift_acc_reg = '0;
logic [INC_NS_W-1:0] ts_inc_ns_reg = '0;
logic [FNS_W-1:0] ts_fns_reg = '0;
logic [32:0] ts_rel_ns_inc_reg = '0;
logic [TS_REL_NS_W-1:0] ts_rel_ns_reg = '0;
logic ts_rel_updated_reg = 1'b0;
logic [TS_TOD_S_W-1:0] ts_tod_s_reg = '0;
logic [TS_TOD_NS_W-1:0] ts_tod_ns_reg = '0;
logic ts_tod_updated_reg = 1'b0;
logic [31:0] ts_tod_offset_ns_reg = '0;
logic [TS_TOD_S_W-1:0] ts_tod_alt_s_reg = '0;
logic [31:0] ts_tod_alt_offset_ns_reg = '0;
logic [7:0] td_update_cnt_reg = '0;
logic td_update_reg = 1'b0;
logic [1:0] td_msg_i_reg = '0;
logic input_ts_tod_ready_reg = 1'b0;
logic input_ts_tod_offset_ready_reg = 1'b0;
logic input_ts_rel_ready_reg = 1'b0;
logic input_ts_rel_offset_ready_reg = 1'b0;
logic input_ts_offset_ready_reg = 1'b0;
logic [17*14-1:0] td_shift_reg = '1;
logic [15:0] pps_gen_fns_reg = '0;
logic [8:0] pps_gen_ns_inc_reg = '0;
logic [30:0] pps_gen_ns_reg = 31'h40000000;
logic [9:0] pps_delay_reg = '0;
logic pps_reg = '0;
logic pps_str_reg = '0;
logic [3:0] update_state_reg = '0;
logic [47:0] adder_a_reg = '0;
logic [47:0] adder_b_reg = '0;
logic adder_cin_reg = '0;
logic [47:0] adder_sum_reg = '0;
logic adder_cout_reg = '0;
logic adder_busy_reg = '0;
assign input_ts_tod_ready = input_ts_tod_ready_reg;
assign input_ts_tod_offset_ready = input_ts_tod_offset_ready_reg;
assign input_ts_rel_ready = input_ts_rel_ready_reg;
assign input_ts_rel_offset_ready = input_ts_rel_offset_ready_reg;
assign input_ts_offset_ready = input_ts_offset_ready_reg;
assign input_period_ready = 1'b1;
assign input_drift_ready = 1'b1;
assign output_pps = pps_reg;
assign output_pps_str = pps_str_reg;
assign ptp_td_sdo = td_shift_reg[0];
always_ff @(posedge clk) begin
drift_apply_reg <= 1'b0;
input_ts_tod_ready_reg <= 1'b0;
input_ts_tod_offset_ready_reg <= 1'b0;
input_ts_rel_ready_reg <= 1'b0;
input_ts_rel_offset_ready_reg <= 1'b0;
input_ts_offset_ready_reg <= 1'b0;
// update and message generation cadence
{td_update_reg, td_update_cnt_reg} <= td_update_cnt_reg + 1;
// latch drift setting
if (input_drift_valid) begin
drift_num_reg <= input_drift_num;
drift_denom_reg <= input_drift_denom;
end
// drift
if (drift_denom_reg != 0) begin
if (drift_cnt_reg != 0) begin
drift_cnt_reg <= drift_cnt_reg - 1;
end else begin
drift_cnt_reg <= drift_denom_reg - 1;
drift_apply_reg <= 1'b1;
end
end else begin
drift_cnt_reg <= 0;
end
drift_cnt_d1_reg <= drift_cnt_reg;
// drift accumulation
if (drift_apply_reg) begin
drift_acc_reg <= drift_acc_reg + 24'(drift_num_reg);
end
// latch period setting
if (input_period_valid) begin
period_ns_reg <= input_period_ns;
period_fns_reg <= input_period_fns;
end
// PPS generation
if (td_update_reg) begin
{pps_gen_ns_inc_reg, pps_gen_fns_reg} <= {period_ns_reg, period_fns_reg[31:16]} + 24'(ts_fns_reg[31:16]);
end else begin
{pps_gen_ns_inc_reg, pps_gen_fns_reg} <= {period_ns_reg, period_fns_reg[31:16]} + 24'(pps_gen_fns_reg);
end
pps_gen_ns_reg <= pps_gen_ns_reg + 31'(pps_gen_ns_inc_reg);
if (!pps_gen_ns_reg[30]) begin
pps_delay_reg <= 14*17 + 32 + 240;
pps_gen_ns_reg[30] <= 1'b1;
end
pps_reg <= 1'b0;
if (ts_tod_ns_reg[29]) begin
pps_str_reg <= 1'b0;
end
if (pps_delay_reg != 0) begin
pps_delay_reg <= pps_delay_reg - 1;
if (pps_delay_reg == 1) begin
pps_reg <= 1'b1;
pps_str_reg <= 1'b1;
end
end
// update state machine
{adder_cout_reg, adder_sum_reg} <= adder_a_reg + adder_b_reg + 48'(adder_cin_reg);
adder_busy_reg <= 1'b0;
// computes the following:
// {ts_inc_ns_reg, ts_fns_reg} = drift_acc_reg + $signed(input_ts_offset_fns) + {period_ns_reg, period_fns_reg} * 256 + ts_fns_reg
// ts_rel_ns_reg = ts_rel_ns_reg + ts_inc_ns_reg + $signed(input_ts_rel_offset_ns);
// ts_tod_ns_reg = ts_tod_ns_reg + ts_inc_ns_reg + $signed(input_ts_tod_offset_ns);
// if that borrowed,
// ts_tod_ns_reg = ts_tod_ns_reg + NS_PER_S
// ts_tod_s_reg = ts_tod_s_reg - 1
// else
// pps_gen_ns_reg = ts_tod_ns_reg - NS_PER_S
// if that did not borrow,
// ts_tod_ns_reg = ts_tod_ns_reg - NS_PER_S
// ts_tod_s_reg = ts_tod_s_reg + 1
// ts_tod_offset_ns_reg = ts_tod_ns_reg - ts_rel_ns_reg
// if ts_tod_ns_reg[29]
// ts_tod_alt_offset_ns_reg = ts_tod_offset_ns_reg - NS_PER_S
// ts_tod_alt_s_reg = ts_tod_s_reg + 1
// else
// ts_tod_alt_offset_ns_reg = ts_tod_offset_ns_reg + NS_PER_S
// ts_tod_alt_s_reg = ts_tod_s_reg - 1
if (!adder_busy_reg) begin
case (update_state_reg)
0: begin
// idle
// set relative timestamp
if (input_ts_rel_valid) begin
ts_rel_ns_reg <= input_ts_rel_ns;
input_ts_rel_ready_reg <= 1'b1;
ts_rel_updated_reg <= 1'b1;
end
// set ToD timestamp
if (input_ts_tod_valid) begin
ts_tod_s_reg <= input_ts_tod_s;
ts_tod_ns_reg <= input_ts_tod_ns;
input_ts_tod_ready_reg <= 1'b1;
ts_tod_updated_reg <= 1'b1;
end
// compute period 1 - add drift and requested offset
if (drift_apply_reg) begin
adder_a_reg <= 48'(drift_acc_reg + drift_num_reg);
end else begin
adder_a_reg <= 48'(drift_acc_reg);
end
adder_b_reg <= '0;
if (input_ts_offset_valid) begin
adder_b_reg <= 48'($signed(input_ts_offset_fns));
end
adder_cin_reg <= 0;
if (td_update_reg) begin
drift_acc_reg <= 0;
input_ts_offset_ready_reg <= input_ts_offset_valid;
update_state_reg <= 1;
adder_busy_reg <= 1'b1;
end else begin
update_state_reg <= 0;
end
end
1: begin
// compute period 2 - add drift and offset to period
adder_a_reg <= adder_sum_reg;
adder_b_reg <= 48'({period_ns_reg, period_fns_reg, 8'd0});
adder_cin_reg <= 0;
update_state_reg <= 2;
adder_busy_reg <= 1'b1;
end
2: begin
// compute next fns
adder_a_reg <= adder_sum_reg;
adder_b_reg <= 48'(ts_fns_reg);
adder_cin_reg <= 0;
update_state_reg <= 3;
adder_busy_reg <= 1'b1;
end
3: begin
// store fns
{ts_inc_ns_reg, ts_fns_reg} <= {adder_cout_reg, adder_sum_reg};
// compute relative timestamp 1 - add previous value and increment
adder_a_reg <= 48'(ts_rel_ns_reg);
adder_b_reg <= 48'({adder_cout_reg, adder_sum_reg} >> FNS_W); // ts_inc_ns_reg
adder_cin_reg <= 0;
update_state_reg <= 4;
adder_busy_reg <= 1'b1;
end
4: begin
// compute relative timestamp 2 - add offset
adder_a_reg <= adder_sum_reg;
adder_b_reg <= '0;
adder_cin_reg <= 0;
// offset relative timestamp if requested
if (input_ts_rel_offset_valid) begin
adder_b_reg <= 48'($signed(input_ts_rel_offset_ns));
input_ts_rel_offset_ready_reg <= 1'b1;
ts_rel_updated_reg <= 1'b1;
end
update_state_reg <= 5;
adder_busy_reg <= 1'b1;
end
5: begin
// store relative timestamp
ts_rel_ns_reg <= adder_sum_reg;
// compute ToD timestamp 1 - add previous value and increment
adder_a_reg <= 48'(ts_tod_ns_reg);
adder_b_reg <= 48'(ts_inc_ns_reg);
adder_cin_reg <= 0;
update_state_reg <= 6;
adder_busy_reg <= 1'b1;
end
6: begin
// compute ToD timestamp 2 - add offset
adder_a_reg <= adder_sum_reg;
adder_b_reg <= '0;
adder_cin_reg <= 0;
// offset ToD timestamp if requested
if (input_ts_tod_offset_valid) begin
adder_b_reg <= 48'($signed(input_ts_tod_offset_ns));
input_ts_tod_offset_ready_reg <= 1'b1;
ts_tod_updated_reg <= 1'b1;
end
update_state_reg <= 7;
adder_busy_reg <= 1'b1;
end
7: begin
// compute ToD timestamp 3 - check for underflow/overflow
ts_tod_ns_reg <= TS_TOD_NS_W'(adder_sum_reg);
if (adder_b_reg[47] && !adder_cout_reg) begin
// borrowed; add 1 billion
adder_a_reg <= adder_sum_reg;
adder_b_reg <= 48'(NS_PER_S);
adder_cin_reg <= 0;
update_state_reg <= 8;
adder_busy_reg <= 1'b1;
end else begin
// did not borrow; subtract 1 billion to check for overflow
adder_a_reg <= adder_sum_reg;
adder_b_reg <= 48'(-NS_PER_S);
adder_cin_reg <= 0;
update_state_reg <= 9;
adder_busy_reg <= 1'b1;
end
end
8: begin
// seconds decrement
ts_tod_ns_reg <= TS_TOD_NS_W'(adder_sum_reg);
pps_gen_ns_reg[30] <= 1'b1;
adder_a_reg <= ts_tod_s_reg;
adder_b_reg <= 48'(-1);
adder_cin_reg <= 0;
update_state_reg <= 10;
adder_busy_reg <= 1'b1;
end
9: begin
// seconds increment
pps_gen_ns_reg <= 31'(adder_sum_reg);
if (!adder_cout_reg) begin
// borrowed; leave seconds alone
adder_a_reg <= ts_tod_s_reg;
adder_b_reg <= '0;
adder_cin_reg <= 0;
end else begin
// did not borrow; increment seconds
ts_tod_ns_reg <= TS_TOD_NS_W'(adder_sum_reg);
adder_a_reg <= ts_tod_s_reg;
adder_b_reg <= 48'(1);
adder_cin_reg <= 0;
end
update_state_reg <= 10;
adder_busy_reg <= 1'b1;
end
10: begin
// store seconds
ts_tod_s_reg <= adder_sum_reg;
// compute offset
adder_a_reg <= 48'(ts_tod_ns_reg);
adder_b_reg <= 48'(~ts_rel_ns_reg);
adder_cin_reg <= 1;
update_state_reg <= 11;
adder_busy_reg <= 1'b1;
end
11: begin
// store offset
ts_tod_offset_ns_reg <= 32'(adder_sum_reg);
adder_a_reg <= adder_sum_reg;
adder_b_reg <= 48'(-NS_PER_S);
adder_cin_reg <= 0;
if (ts_tod_ns_reg[29:27] == 3'b111) begin
// latter portion of second; compute offset for next second
adder_b_reg <= 48'(-NS_PER_S);
update_state_reg <= 12;
adder_busy_reg <= 1'b1;
end else begin
// former portion of second; compute offset for previous second
adder_b_reg <= 48'(NS_PER_S);
update_state_reg <= 14;
adder_busy_reg <= 1'b1;
end
end
12: begin
// store alternate offset for next second
ts_tod_alt_offset_ns_reg <= 32'(adder_sum_reg);
adder_a_reg <= ts_tod_s_reg;
adder_b_reg <= 48'(1);
adder_cin_reg <= 0;
update_state_reg <= 13;
adder_busy_reg <= 1'b1;
end
13: begin
// store alternate second for next second
ts_tod_alt_s_reg <= adder_sum_reg;
update_state_reg <= 0;
end
14: begin
// store alternate offset for previous second
ts_tod_alt_offset_ns_reg <= 32'(adder_sum_reg);
adder_a_reg <= ts_tod_s_reg;
adder_b_reg <= 48'(-1);
adder_cin_reg <= 0;
update_state_reg <= 15;
adder_busy_reg <= 1'b1;
end
15: begin
// store alternate second for previous second
ts_tod_alt_s_reg <= adder_sum_reg;
update_state_reg <= 0;
end
default: begin
// invalid state; return to idle
update_state_reg <= 0;
end
endcase
end
// time distribution message generation
td_shift_reg <= {1'b1, td_shift_reg[17*14-1:1]};
if (td_update_reg) begin
// word 0: control
td_shift_reg[17*0+0 +: 1] <= 1'b0;
td_shift_reg[17*0+1 +: 16] <= 0;
td_shift_reg[17*0+1+0 +: 4] <= 4'(td_msg_i_reg);
td_shift_reg[17*0+1+8 +: 1] <= ts_rel_updated_reg;
td_shift_reg[17*0+1+9 +: 1] <= ts_tod_s_reg[0];
ts_rel_updated_reg <= 1'b0;
case (td_msg_i_reg)
2'd0: begin
// msg 0 word 1: current ToD ns 15:0
td_shift_reg[17*1+0 +: 1] <= 1'b0;
td_shift_reg[17*1+1 +: 16] <= ts_tod_ns_reg[15:0];
// msg 0 word 2: current ToD ns 29:16
td_shift_reg[17*2+0 +: 1] <= 1'b0;
td_shift_reg[17*2+1+0 +: 15] <= 15'(ts_tod_ns_reg[29:16]);
td_shift_reg[17*2+1+15 +: 1] <= ts_tod_updated_reg;
ts_tod_updated_reg <= 1'b0;
// msg 0 word 3: current ToD seconds 15:0
td_shift_reg[17*3+0 +: 1] <= 1'b0;
td_shift_reg[17*3+1 +: 16] <= ts_tod_s_reg[15:0];
// msg 0 word 4: current ToD seconds 31:16
td_shift_reg[17*4+0 +: 1] <= 1'b0;
td_shift_reg[17*4+1 +: 16] <= ts_tod_s_reg[31:16];
// msg 0 word 5: current ToD seconds 47:32
td_shift_reg[17*5+0 +: 1] <= 1'b0;
td_shift_reg[17*5+1 +: 16] <= ts_tod_s_reg[47:32];
td_msg_i_reg <= 2'd1;
end
2'd1: begin
// msg 1 word 1: current ToD ns offset 15:0
td_shift_reg[17*1+0 +: 1] <= 1'b0;
td_shift_reg[17*1+1 +: 16] <= ts_tod_offset_ns_reg[15:0];
// msg 1 word 2: current ToD ns offset 31:16
td_shift_reg[17*2+0 +: 1] <= 1'b0;
td_shift_reg[17*2+1 +: 16] <= ts_tod_offset_ns_reg[31:16];
// msg 1 word 3: drift num
td_shift_reg[17*3+0 +: 1] <= 1'b0;
td_shift_reg[17*3+1 +: 16] <= drift_num_reg;
// msg 1 word 4: drift denom
td_shift_reg[17*4+0 +: 1] <= 1'b0;
td_shift_reg[17*4+1 +: 16] <= drift_denom_reg;
// msg 1 word 5: drift state
td_shift_reg[17*5+0 +: 1] <= 1'b0;
td_shift_reg[17*5+1 +: 16] <= drift_cnt_d1_reg;
td_msg_i_reg <= 2'd2;
end
2'd2: begin
// msg 2 word 1: alternate ToD ns offset 15:0
td_shift_reg[17*1+0 +: 1] <= 1'b0;
td_shift_reg[17*1+1 +: 16] <= ts_tod_alt_offset_ns_reg[15:0];
// msg 2 word 2: alternate ToD ns offset 31:16
td_shift_reg[17*2+0 +: 1] <= 1'b0;
td_shift_reg[17*2+1 +: 16] <= ts_tod_alt_offset_ns_reg[31:16];
// msg 2 word 3: alternate ToD seconds 15:0
td_shift_reg[17*3+0 +: 1] <= 1'b0;
td_shift_reg[17*3+1 +: 16] <= ts_tod_alt_s_reg[15:0];
// msg 2 word 4: alternate ToD seconds 31:16
td_shift_reg[17*4+0 +: 1] <= 1'b0;
td_shift_reg[17*4+1 +: 16] <= ts_tod_alt_s_reg[31:16];
// msg 2 word 5: alternate ToD seconds 47:32
td_shift_reg[17*5+0 +: 1] <= 1'b0;
td_shift_reg[17*5+1 +: 16] <= ts_tod_alt_s_reg[47:32];
td_msg_i_reg <= 2'd0;
end
default: begin
td_shift_reg[17*1+0 +: 1] <= 1'b0;
td_shift_reg[17*1+1 +: 16] <= '0;
td_shift_reg[17*2+0 +: 1] <= 1'b0;
td_shift_reg[17*2+1 +: 16] <= '0;
td_shift_reg[17*3+0 +: 1] <= 1'b0;
td_shift_reg[17*3+1 +: 16] <= '0;
td_shift_reg[17*4+0 +: 1] <= 1'b0;
td_shift_reg[17*4+1 +: 16] <= '0;
td_shift_reg[17*5+0 +: 1] <= 1'b0;
td_shift_reg[17*5+1 +: 16] <= '0;
td_msg_i_reg <= 2'd0;
end
endcase
// word 6: current fns 15:0
td_shift_reg[17*6+0 +: 1] <= 1'b0;
td_shift_reg[17*6+1 +: 16] <= ts_fns_reg[15:0];
// word 7: current fns 31:16
td_shift_reg[17*7+0 +: 1] <= 1'b0;
td_shift_reg[17*7+1 +: 16] <= ts_fns_reg[31:16];
// word 8: current ns 15:0
td_shift_reg[17*8+0 +: 1] <= 1'b0;
td_shift_reg[17*8+1 +: 16] <= ts_rel_ns_reg[15:0];
// word 9: current ns 31:16
td_shift_reg[17*9+0 +: 1] <= 1'b0;
td_shift_reg[17*9+1 +: 16] <= ts_rel_ns_reg[31:16];
// word 10: current ns 47:32
td_shift_reg[17*10+0 +: 1] <= 1'b0;
td_shift_reg[17*10+1 +: 16] <= ts_rel_ns_reg[47:32];
// word 11: current phase increment fns 15:0
td_shift_reg[17*11+0 +: 1] <= 1'b0;
td_shift_reg[17*11+1 +: 16] <= period_fns_reg[15:0];
// word 12: current phase increment fns 31:16
td_shift_reg[17*12+0 +: 1] <= 1'b0;
td_shift_reg[17*12+1 +: 16] <= period_fns_reg[31:16];
// word 13: current phase increment ns 7:0 + crc
td_shift_reg[17*13+0 +: 1] <= 1'b0;
td_shift_reg[17*13+1+0 +: 8] <= period_ns_reg[7:0];
td_shift_reg[17*13+1+8 +: 8] <= '0;
end
if (rst) begin
period_ns_reg <= PERIOD_NS_W'(PERIOD_NS);
period_fns_reg <= FNS_W'(PERIOD_FNS);
drift_num_reg <= 16'(PERIOD_FNS_REM);
drift_denom_reg <= 16'(PERIOD_NS_DENOM);
drift_cnt_reg <= '0;
drift_acc_reg <= '0;
ts_fns_reg <= '0;
ts_rel_ns_reg <= '0;
ts_rel_updated_reg <= '0;
ts_tod_s_reg <= '0;
ts_tod_ns_reg <= '0;
ts_tod_updated_reg <= '0;
pps_gen_ns_reg[30] <= 1'b1;
pps_delay_reg <= '0;
pps_reg <= '0;
pps_str_reg <= '0;
td_update_cnt_reg <= '0;
td_update_reg <= 1'b0;
td_msg_i_reg <= '0;
td_shift_reg <= '1;
end
end
endmodule
`resetall