| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- `timescale 1ns/1ps
- // -----------------------------------------------------------------------------
- // pwm_trace_measure
- //
- // Passive digital-signal measurement core.
- //
- // Measures:
- // 1) edge_count : total rising-edge count since reset
- // 2) period_ticks : clocks between two consecutive rising edges
- // 3) high_ticks : clocks that the input stayed high in that same period
- //
- // With a 25 MHz FPGA clock:
- // frequency_hz = 25_000_000 / period_ticks
- // duty_percent = high_ticks * 100 / period_ticks
- //
- // The observed signal is treated as asynchronous to clk, therefore a
- // two-flop synchronizer is used before edge detection.
- // -----------------------------------------------------------------------------
- module pwm_trace_measure #(
- parameter integer CLK_HZ = 25_000_000,
- parameter integer NO_SIGNAL_MS = 100
- )(
- input wire clk,
- input wire reset_n,
- input wire signal_in,
- output reg [31:0] edge_count,
- output reg [31:0] period_ticks,
- output reg [31:0] high_ticks,
- output reg measurement_valid,
- output reg signal_present,
- output wire signal_level
- );
- localparam integer NO_SIGNAL_TICKS =
- (CLK_HZ / 1000) * NO_SIGNAL_MS;
- // Synchronizer + delayed sample for edge detection.
- reg signal_meta;
- reg signal_sync;
- reg signal_sync_d;
- wire rising_edge = signal_sync & ~signal_sync_d;
- wire falling_edge = ~signal_sync & signal_sync_d;
- // Raw synchronized level is reported separately from period validity so
- // static LOW and static HIGH remain distinguishable after edge timeout.
- assign signal_level = signal_sync;
- reg [31:0] period_counter;
- reg [31:0] high_counter;
- reg [31:0] no_signal_counter;
- reg have_rise;
- reg have_high;
- always @(posedge clk or negedge reset_n) begin
- if (!reset_n) begin
- signal_meta <= 1'b0;
- signal_sync <= 1'b0;
- signal_sync_d <= 1'b0;
- end else begin
- signal_meta <= signal_in;
- signal_sync <= signal_meta;
- signal_sync_d <= signal_sync;
- end
- end
- always @(posedge clk or negedge reset_n) begin
- if (!reset_n) begin
- edge_count <= 32'd0;
- period_ticks <= 32'd0;
- high_ticks <= 32'd0;
- measurement_valid <= 1'b0;
- signal_present <= 1'b0;
- period_counter <= 32'd0;
- high_counter <= 32'd0;
- no_signal_counter <= 32'd0;
- have_rise <= 1'b0;
- have_high <= 1'b0;
- end else begin
- // ---------------------------------------------------------
- // Rising edge:
- // - counts one pulse
- // - closes the previous complete period
- // - starts a new period/high counter
- // ---------------------------------------------------------
- if (rising_edge) begin
- edge_count <= edge_count + 1'b1;
- signal_present <= 1'b1;
- no_signal_counter <= 32'd0;
- if (have_rise) begin
- period_ticks <= period_counter;
- // high_ticks was latched by the previous falling edge,
- // therefore period_ticks + high_ticks belong to the
- // same just-finished PWM period.
- if (have_high)
- measurement_valid <= 1'b1;
- end
- period_counter <= 32'd1;
- high_counter <= 32'd1;
- have_rise <= 1'b1;
- have_high <= 1'b0;
- end else begin
- if (have_rise && period_counter != 32'hFFFF_FFFF)
- period_counter <= period_counter + 1'b1;
- if (have_rise && signal_sync &&
- high_counter != 32'hFFFF_FFFF)
- high_counter <= high_counter + 1'b1;
- // -----------------------------------------------------
- // No-signal timeout.
- // Keep edge_count, but clear frequency/duty information.
- // -----------------------------------------------------
- if (signal_present) begin
- if (no_signal_counter >= NO_SIGNAL_TICKS - 1) begin
- no_signal_counter <= 32'd0;
- signal_present <= 1'b0;
- measurement_valid <= 1'b0;
- period_ticks <= 32'd0;
- high_ticks <= 32'd0;
- period_counter <= 32'd0;
- high_counter <= 32'd0;
- have_rise <= 1'b0;
- have_high <= 1'b0;
- end else begin
- no_signal_counter <= no_signal_counter + 1'b1;
- end
- end
- end
- // ---------------------------------------------------------
- // Falling edge closes the HIGH portion of the current PWM
- // period. The value is paired with period_ticks on the next
- // rising edge.
- // ---------------------------------------------------------
- if (falling_edge && have_rise) begin
- high_ticks <= high_counter;
- have_high <= 1'b1;
- end
- end
- end
- endmodule
|