/*
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                                                                                                 ┃
┃          Program Name: RefMeas.v                                                                ┃
┃                Author: Michael Krämer                                                           ┃
┃                 Email: Michael@dk8pp.de                                                         ┃
┃                  Date: 27.07.2025                                                               ┃
┃                  Tool: Gowin FPGA Designer V1.9.11.01 Education                                 ┃
┃                Device: GW5A-LV25MG121 Typ A                                                     ┃
┃               Version: 0.1                                                                      ┃
┃        Program status: still testing                                                            ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  Purpose: Measure the phase difference between two signals and their frequencies,               ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  © Copyright 2025-2026 Michael Krämer, DK8PP                                                    ┃
┃                                                                                                 ┃
┃  This program is free software: you can redistribute it and/or modify it under the terms of     ┃
┃  the GNU Lesser General Public License as published by the Free Software Foundation, either     ┃
┃  version 3 of the License, or (at your option) any later version.                               ┃
┃                                                                                                 ┃
┃  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;      ┃
┃  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.      ┃
┃  See the GNU Lesser General Public License for more details.                                    ┃
┃                                                                                                 ┃
┃  You should have received a copy of the GNU Lesser General Public License along with this       ┃
┃  program.   If not, see <http://www.gnu.org/licenses/>.                                         ┃
┃                                                                                                 ┃
┃  ============================================================================================   ┃
┃                                                                                                 ┃
┃  Dieses Programm ist Freie Software: Sie können es unter den Bedingungen der GNU Lesser         ┃
┃  General Public License, wie von der Free Software Foundation, Version 3 der Lizenz oder        ┃
┃  (nach Ihrer Wahl) jeder neueren veröffentlichten Version, weiter verteilen und/oder            ┃
┃  modifizieren.                                                                                  ┃
┃                                                                                                 ┃
┃  Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch OHNE   ┃
┃  JEDE GEWÄHR,; sogar ohne die implizite Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN        ┃
┃  BESTIMMTEN ZWECK. Siehe die GNU Lesser General Public License für weitere Einzelheiten.        ┃
┃                                                                                                 ┃
┃  Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit diesem Programm      ┃
┃  erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.                             ┃
┃                                                                                                 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

This module determines the phase shift between two signals, specifically between the forward
<sig_forward_in> and the reflected <sig_reflected_in> signals from a reflection measurement
device, typically a Sontheimer coupler. It does so by generating an exclusive or between them and
counting the number of high samples during a specic interval. It is assumed that the sampling
frequency is asynchronous to the signals and that the average count over a long interval
represents the phase difference. If the input signals are precisely in phase, their exclusive or
is always zero and therefore the accumulated result in the <phasediff> register is also (close to)
zero. If the input signals are fully out of phase, their exclusive or is always one, resulting in
a maximum accumulated count.

This method leads to an ambiguity as it cannot distingish between positive and negative phase
shifts. +90° phase shift brings the same result as a shift of -90°. In order to determine which of
the clocks is ahead of the other one, i.e. the sign of the phase shift, the level of the reflected
clock signal is determined at the rising edge of the forward clock signal. Equally the level of
the forward clock signal is determined at the rising edge of the reflected clock signal. Two
counters, namely <f_leads_r> and <r_leads_f>, accumulate the levels during the same measurment
interval as the phase shift measurement described above. The top module determines how to use
these results. Typically a simple magnitude comparator will be sufficiently precise to determine
the phase shift direction.

The total number of rising forward and reflected clock edges during the measurement interval are
accumulated in the <f_frequency_reg> and <r_frequency_reg> counters respectively. These counts are
used to calculate the frequency of the clocks. It can also serve as proof that there was an input
signal at all. This number may also be used to qualify the count of the <f_leads_r> and
<r_leads_f> counters.

Due to a hardware flaw of the first prototype, the detection level of the input signals is not
exactly in the middle of the signals. That leads to a shift in the detection of the high and low
phases, which gets worse for small signal levels. The counters <cnt_fhigh_reg> and <cnt_flow_reg>
measure the duration of the positive half wave of the respective signal. Ideally at the end of a
measurement cycle, their counts should half of the interval count. Their deviation from 50% is a
measure of the signal quality. These counters may be removed when fixed hardware is available.

<sclk_in> is the reference clock and it is always available during measurement. At other times,
<sclk_in> may be disabled to reduce power consumption.

Forward and reflected clocks have essentially the same frequency, but their phase is shifted
between 0° and 359°. For the currently intended application in a reflection measurement device,
their frequency spans a little more than the shortwave bands, from roundabout 1 MHz to 50 MHz.
Both clocks, forward and reflected, may or may not be active, but there is no reflected clock
without a forward clock.

The sampling frequency <sclk_in> is determined by the capabilities of the FPGA and typically in
the order of 200 MHz. It must at least be twice as high as the maximum measurement frequency but
it should be low enough to prevent excessive power consumption.

In all typical cases, the clocks are asynchronous to each other, but in exceptional cases,
<sclk_in> may be synchronous to <sig_forward_in> and <sig_reflected_in>, just by coincidence.
Tests must show if the built-in spread spectrum oscillator can solve any issues caused by such
cases.

*/

module Reflection_Measurement
    #(parameter CNTR_BITSIZE = 32,      /* width of counters */
      ADSIZE = 5,                       /* default size of address decoder */
      SAMPLE_COUNT = 32'd2_000_000      /* default sample count per interval, must be an integral multiple of 1000 */
    )
    (input  wire cpuclk,                /* cpu clock */
     input  wire reset,                 /* reset input, active high */
     input  wire [ADSIZE-1:0] addr_in,  /* address input */
     input  wire [31:0] data_in,        /* data input */
     output wire [31:0] data_out,       /* data output */
     input  wire mod_sel,               /* module select */
     input  wire cpu_write,             /* CPU write cycle */
     output wire [6:0] testsig,

     input  wire sclk_in,               /* sampling clock */
     input  wire sig_forward_in,        /* forward signal from coupler */
     input  wire sig_reflected_in       /* reflected signal from coupler */
     ) ;

        reg     [CNTR_BITSIZE-1:0] phasediff_reg ;
        reg     [CNTR_BITSIZE-1:0] f_leads_r_reg ;
        reg     [CNTR_BITSIZE-1:0] r_leads_f_reg ;
        reg     [CNTR_BITSIZE-1:0] f_frequency_reg ;    /* register for forward frequency */
        reg     [CNTR_BITSIZE-1:0] r_frequency_reg ;    /* register for reflected frequency */
        reg     [CNTR_BITSIZE-1:0] samplinginterval ;   /* sampling clock count */
        reg     [CNTR_BITSIZE-1:0] cnt_fhigh_reg ;      /* count forward signal high */
        reg     [CNTR_BITSIZE-1:0] cnt_flow_reg ;       /* count forward signal low */
        reg     [CNTR_BITSIZE-1:0] cnt_rhigh_reg ;      /* count reflected signal high */
        reg     [CNTR_BITSIZE-1:0] cnt_rlow_reg ;       /* count reflected signal low */
        reg     [1:0] sig_f ;                           /* synchronized forward signal */
        reg     [1:0] sig_r ;                           /* synchronized reflected signal */
        reg     sync_phasediff ;                        /* synchronous phase_diff */
        reg     active_measurement ;                    /* active measurement cycle */

        reg     valid_result ;                          /* valid result in CPU registers */
        reg     start_cycle ;                           /* set to start a measurement cycle */
        reg     [CNTR_BITSIZE-1:0] cpureg_samplinginterval ; /* sampling clock count per interval */

        wire    rising_sigf ;                           /* rising edge on forward signal detected */
        wire    rising_sigr ;                           /* rising edge on reflected signal detected */
        wire    phase_diff ;                            /* phase difference between sig_forward_in and sig_reflected_in */
        wire    statusreg = {31'h0, valid_result} ;
        wire    controlreg = {31'h0, start_cycle} ;

assign rising_sigf = (sig_f == 2'b01) ;
assign rising_sigr = (sig_r == 2'b01) ;
assign phase_diff = sig_forward_in ^ sig_reflected_in ; /* true if signals differ */

//wire t1 = rising_sigf && (sig_r[0] == 0) ;
//wire t2 = rising_sigr && (sig_f[0] == 0) ;
wire t1 = ~sig_f[1] & sig_f[0] & ~sig_r[0] ;
wire t2 = ~sig_r[1] & sig_r[0] & ~sig_f[0] ;
wire t3 = t1 | t2 ;

assign testsig[6:0] = {sig_forward_in, sig_reflected_in, active_measurement, rising_sigr, sig_r[0], t1, t2} ;
//assign testsig[6:0] = {sig_forward_in, sig_reflected_in, t3, rising_sigr, t1, t2, 1'b0} ;

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Address mapping                                                                           │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯

Reflection Measurement (RM) register address offsets:

+RM_PDC_OFFSET:  Phase Difference Counter, PDC (R)
    The exclusive or of sig_forward_in and sig_reflected_in is true, if the input signals differ
    in level. This output signal is sampled with the sampling clock and the Phase Difference
    Counter (PDC) 'phasediff' is incremented each time when they differ. Therefore, at the end of
    a measurement interval, 'phasediff' holds the number of sample clocks in which sig_forward_in
    and sig_reflected_in had different levels.

+RM_CREG_OFFSET: Control Register, SREG (R/W)
    This is the control register.
    bit 0: start        write '1' to this bit to start a measurement cycle

+RM_SREG_OFFSET: Status Register, SREG (R)
    This is the status register.
    bit 0: valid        measurement completed, valid results available in the result registers

+RM_INTERVAL_OFFSET: Reflection Measurement interval, (R/W)
    This register sets the duration of the reflection measurement in number of sample clocks-1. It
    is a read/write register and it must be initialized by the CPU before measurement cycle is
    started. It can be read or written while a measurement cycle is active, but it has no effect
    until the next cycle starts.

+RM_EC1_OFFSET: Edge Counter for leading forward edges, EC1 (R)
    This register counts the number of rising edges of the forward signal, while the reflected
    signal is low.

+RM_EC2_OFFSET: Edge Counter for leading reflected edges, EC2 (R)
    This register counts the number of rising edges of the reflected signal, while the forward
    signal is low.

+RM_FC1_OFFSET: Forward frequency, FC1 (R)
    sig_forward_in is registered with each rising edge of the sampling clock. If a rising edge of
    sig_forward_in is detected, the FC1 accumulator is incremented. When measurement is complete,
    this register holds the number of rising sig_forward_in edges during the interval.

+RM_FC2_OFFSET: Reflected frequency, FC2 (R)
    sig_reflected_in is registered with each rising edge of the sampling clock. If a rising edge
    of sig_reflected_in is detected, the FC2 accumulator is incremented. When measurement is
    complete, this register holds the number of rising sig_reflected_in edges during the interval.

+RM_FHIGH_OFFSET: high time of forward signal, FH (R)
    sampling clock count during a measurement interval with <sig_forward_in> high

+RM_FLOW_OFFSET: low time of forward signal, FL (R)
    sampling clock count during a measurement interval with <sig_forward_in> low

+RM_RHIGH_OFFSET: high time of reflected signal, RH (R)
    sampling clock count during a measurement interval with <sig_reflected_in> high

+RM_RLOW_OFFSET: low time of reflected signal, RL (R)
    sampling clock count during a measurement interval with <sig_reflected_in> low

*/

localparam RM_PDC_OFFSET =          32'h00 ;    /* Phase Difference Counter offset */
localparam RM_CREG_OFFSET =         32'h04 ;    /* Control Register offset */
localparam RM_SREG_OFFSET =         32'h08 ;    /* Status Register offset */
localparam RM_INTERVAL_OFFSET =     32'h0c ;    /* Measurement interval time */
localparam RM_EC1_OFFSET =          32'h10 ;    /* Edge Counter 1 offset */
localparam RM_EC2_OFFSET =          32'h14 ;    /* Edge Counter 2 offset */
localparam RM_FC1_OFFSET =          32'h18 ;    /* Frequency Counter 1 offset, forward frequency */
localparam RM_FC2_OFFSET =          32'h1c ;    /* Frequency Counter 2 offset, reflected frequency */
localparam RM_FHIGH_OFFSET =        32'h20 ;    /* Forward signal high counter */
localparam RM_FLOW_OFFSET =         32'h24 ;    /* Forward signal low counter */
localparam RM_RHIGH_OFFSET =        32'h28 ;    /* Reflected signal high counter */
localparam RM_RLOW_OFFSET =         32'h2c ;    /* Reflected signal low counter */

wire [ADSIZE-1:0] addr_pdc =        RM_PDC_OFFSET ;
wire [ADSIZE-1:0] addr_creg =       RM_CREG_OFFSET ;
wire [ADSIZE-1:0] addr_sreg =       RM_SREG_OFFSET ;
wire [ADSIZE-1:0] addr_interval =   RM_INTERVAL_OFFSET ;
wire [ADSIZE-1:0] addr_ec1 =        RM_EC1_OFFSET ;
wire [ADSIZE-1:0] addr_ec2 =        RM_EC2_OFFSET ;
wire [ADSIZE-1:0] addr_fc1 =        RM_FC1_OFFSET ;
wire [ADSIZE-1:0] addr_fc2 =        RM_FC2_OFFSET ;
wire [ADSIZE-1:0] addr_fhigh =      RM_FHIGH_OFFSET ;
wire [ADSIZE-1:0] addr_flow =       RM_FLOW_OFFSET ;
wire [ADSIZE-1:0] addr_rhigh =      RM_RHIGH_OFFSET ;
wire [ADSIZE-1:0] addr_rlow =       RM_RLOW_OFFSET ;

wire wr_creg =      cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_creg) ;      /* write control register */
wire wr_interval =  cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_interval) ;  /* write measurement interval time */

wire rd_pdc =       ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_pdc) ;
wire rd_creg =      ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_creg) ;
wire rd_sreg =      ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_sreg) ;
wire rd_interval =  ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_interval) ;
wire rd_ec1 =       ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_ec1) ;
wire rd_ec2 =       ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_ec2) ;
wire rd_fc1 =       ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_fc1) ;
wire rd_fc2 =       ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_fc2) ;
wire rd_fhigh =     ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_fhigh) ;
wire rd_flow =      ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_flow) ;
wire rd_rhigh =     ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_rhigh) ;
wire rd_rlow =      ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_rlow) ;

/* read data */
assign data_out = rd_pdc      ? {{32-CNTR_BITSIZE{1'b0}}, phasediff_reg} :
                  rd_sreg     ? statusreg :
                  rd_creg     ? controlreg :
                  rd_interval ? {{32-CNTR_BITSIZE{1'b0}}, cpureg_samplinginterval} :
                  rd_ec1      ? {{32-CNTR_BITSIZE{1'b0}}, f_leads_r_reg} :
                  rd_ec2      ? {{32-CNTR_BITSIZE{1'b0}}, r_leads_f_reg} :
                  rd_fc1      ? {{32-CNTR_BITSIZE{1'b0}}, f_frequency_reg} :
                  rd_fc2      ? {{32-CNTR_BITSIZE{1'b0}}, r_frequency_reg} :
                  rd_fhigh    ? {{32-CNTR_BITSIZE{1'b0}}, cnt_fhigh_reg} :
                  rd_flow     ? {{32-CNTR_BITSIZE{1'b0}}, cnt_flow_reg} :
                  rd_rhigh    ? {{32-CNTR_BITSIZE{1'b0}}, cnt_rhigh_reg} :
                  rd_rlow     ? {{32-CNTR_BITSIZE{1'b0}}, cnt_rlow_reg} :
                  32'h00 ;

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       cpuclk clock domain                                                                       │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯

CPU access to the reflection measurement.

Note:
    No synchronization to the sclk-domain. Accesses to the reflection measurement registers can
    only be made while the measurement is idle.
    Assuming sclk frequency is at least double cpuclk frequency

*/
always @(posedge cpuclk)
begin
   if (reset)
   begin
      cpureg_samplinginterval <= SAMPLE_COUNT ;
      start_cycle <= 0 ;
   end
   else
   begin
      if (wr_interval) cpureg_samplinginterval <= data_in[CNTR_BITSIZE-1:0] ; /* write sampling interval register */
      if (wr_creg) start_cycle <= data_in[0] ; /* write bit 0 to '1' to start a measurement cycle */
      else start_cycle <= 0 ; /* clear again after write cycle */
   end
end

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       sclk_in clock domain                                                                      │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
always @(posedge sclk_in)
begin
   if (reset)
   begin
      phasediff_reg <= 0 ;
      f_leads_r_reg <= 0 ;
      r_leads_f_reg <= 0 ;
      f_frequency_reg <= 0 ;
      r_frequency_reg <= 0 ;
      samplinginterval <= 0 ;
      cnt_fhigh_reg <= 0 ;
      cnt_flow_reg <= 0 ;
      cnt_rhigh_reg <= 0 ;
      cnt_rlow_reg <= 0 ;
      active_measurement <= 0 ;
      valid_result <= 0 ;
   end
   else
   begin
      sig_f <= {sig_f[0],sig_forward_in} ;
      sig_r <= {sig_r[0],sig_reflected_in} ;
      sync_phasediff <= phase_diff ;

      if (!active_measurement)
      begin
         if (start_cycle)
         begin
            phasediff_reg <= 0 ;
            f_leads_r_reg <= 0 ;
            r_leads_f_reg <= 0 ;
            f_frequency_reg <= 0 ;
            r_frequency_reg <= 0 ;
            samplinginterval <= cpureg_samplinginterval ;
            cnt_fhigh_reg <= 0 ;
            cnt_flow_reg <= 0 ;
            cnt_rhigh_reg <= 0 ;
            cnt_rlow_reg <= 0 ;
            active_measurement <= 1 ;
            valid_result <= 0 ;
         end
      end

      if (active_measurement)
      begin

/* Phase Difference Counter (PDC), increment if levels differ */
         if (sync_phasediff)
         begin
            phasediff_reg <= phasediff_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         end

/* Edge Counter (EC), measure leading or trailing phase shift */
         if (rising_sigf && (sig_r[0] == 0))
         begin
            f_leads_r_reg <= f_leads_r_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         end
         if (rising_sigr && (sig_f[0] == 0))
         begin
            r_leads_f_reg <= r_leads_f_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         end

/* Frequency Counter 1 (FC1), measure forward frequency */
         if (rising_sigf)
         begin
            f_frequency_reg <= f_frequency_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         end

/* Frequency Counter 2 (FC2), measure reflected frequency */
         if (rising_sigr)
         begin
            r_frequency_reg <= r_frequency_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         end

/* count sig_f high/low pulse width */
         if (sig_f[1]) cnt_fhigh_reg <= cnt_fhigh_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         else          cnt_flow_reg  <= cnt_flow_reg  + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;

/* count sig_r high/low pulse width */
         if (sig_r[1]) cnt_rhigh_reg <= cnt_rhigh_reg + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         else          cnt_rlow_reg  <= cnt_rlow_reg  + {{CNTR_BITSIZE-1{1'b0}},1'b1} ;

         if (samplinginterval == 0)
         begin
            active_measurement <= 0 ;
            valid_result <= 1 ;
         end
         else
         begin
            samplinginterval <= samplinginterval - {{CNTR_BITSIZE-1{1'b0}},1'b1} ;
         end

      end

   end
end

endmodule

