pwm_trace_measure.v 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. `timescale 1ns/1ps
  2. // -----------------------------------------------------------------------------
  3. // pwm_trace_measure
  4. //
  5. // Passive digital-signal measurement core.
  6. //
  7. // Measures:
  8. // 1) edge_count : total rising-edge count since reset
  9. // 2) period_ticks : clocks between two consecutive rising edges
  10. // 3) high_ticks : clocks that the input stayed high in that same period
  11. //
  12. // With a 25 MHz FPGA clock:
  13. // frequency_hz = 25_000_000 / period_ticks
  14. // duty_percent = high_ticks * 100 / period_ticks
  15. //
  16. // The observed signal is treated as asynchronous to clk, therefore a
  17. // two-flop synchronizer is used before edge detection.
  18. // -----------------------------------------------------------------------------
  19. module pwm_trace_measure #(
  20. parameter integer CLK_HZ = 25_000_000,
  21. parameter integer NO_SIGNAL_MS = 100
  22. )(
  23. input wire clk,
  24. input wire reset_n,
  25. input wire signal_in,
  26. output reg [31:0] edge_count,
  27. output reg [31:0] period_ticks,
  28. output reg [31:0] high_ticks,
  29. output reg measurement_valid,
  30. output reg signal_present,
  31. output wire signal_level
  32. );
  33. localparam integer NO_SIGNAL_TICKS =
  34. (CLK_HZ / 1000) * NO_SIGNAL_MS;
  35. // Synchronizer + delayed sample for edge detection.
  36. reg signal_meta;
  37. reg signal_sync;
  38. reg signal_sync_d;
  39. wire rising_edge = signal_sync & ~signal_sync_d;
  40. wire falling_edge = ~signal_sync & signal_sync_d;
  41. // Raw synchronized level is reported separately from period validity so
  42. // static LOW and static HIGH remain distinguishable after edge timeout.
  43. assign signal_level = signal_sync;
  44. reg [31:0] period_counter;
  45. reg [31:0] high_counter;
  46. reg [31:0] no_signal_counter;
  47. reg have_rise;
  48. reg have_high;
  49. always @(posedge clk or negedge reset_n) begin
  50. if (!reset_n) begin
  51. signal_meta <= 1'b0;
  52. signal_sync <= 1'b0;
  53. signal_sync_d <= 1'b0;
  54. end else begin
  55. signal_meta <= signal_in;
  56. signal_sync <= signal_meta;
  57. signal_sync_d <= signal_sync;
  58. end
  59. end
  60. always @(posedge clk or negedge reset_n) begin
  61. if (!reset_n) begin
  62. edge_count <= 32'd0;
  63. period_ticks <= 32'd0;
  64. high_ticks <= 32'd0;
  65. measurement_valid <= 1'b0;
  66. signal_present <= 1'b0;
  67. period_counter <= 32'd0;
  68. high_counter <= 32'd0;
  69. no_signal_counter <= 32'd0;
  70. have_rise <= 1'b0;
  71. have_high <= 1'b0;
  72. end else begin
  73. // ---------------------------------------------------------
  74. // Rising edge:
  75. // - counts one pulse
  76. // - closes the previous complete period
  77. // - starts a new period/high counter
  78. // ---------------------------------------------------------
  79. if (rising_edge) begin
  80. edge_count <= edge_count + 1'b1;
  81. signal_present <= 1'b1;
  82. no_signal_counter <= 32'd0;
  83. if (have_rise) begin
  84. period_ticks <= period_counter;
  85. // high_ticks was latched by the previous falling edge,
  86. // therefore period_ticks + high_ticks belong to the
  87. // same just-finished PWM period.
  88. if (have_high)
  89. measurement_valid <= 1'b1;
  90. end
  91. period_counter <= 32'd1;
  92. high_counter <= 32'd1;
  93. have_rise <= 1'b1;
  94. have_high <= 1'b0;
  95. end else begin
  96. if (have_rise && period_counter != 32'hFFFF_FFFF)
  97. period_counter <= period_counter + 1'b1;
  98. if (have_rise && signal_sync &&
  99. high_counter != 32'hFFFF_FFFF)
  100. high_counter <= high_counter + 1'b1;
  101. // -----------------------------------------------------
  102. // No-signal timeout.
  103. // Keep edge_count, but clear frequency/duty information.
  104. // -----------------------------------------------------
  105. if (signal_present) begin
  106. if (no_signal_counter >= NO_SIGNAL_TICKS - 1) begin
  107. no_signal_counter <= 32'd0;
  108. signal_present <= 1'b0;
  109. measurement_valid <= 1'b0;
  110. period_ticks <= 32'd0;
  111. high_ticks <= 32'd0;
  112. period_counter <= 32'd0;
  113. high_counter <= 32'd0;
  114. have_rise <= 1'b0;
  115. have_high <= 1'b0;
  116. end else begin
  117. no_signal_counter <= no_signal_counter + 1'b1;
  118. end
  119. end
  120. end
  121. // ---------------------------------------------------------
  122. // Falling edge closes the HIGH portion of the current PWM
  123. // period. The value is paired with period_ticks on the next
  124. // rising edge.
  125. // ---------------------------------------------------------
  126. if (falling_edge && have_rise) begin
  127. high_ticks <= high_counter;
  128. have_high <= 1'b1;
  129. end
  130. end
  131. end
  132. endmodule