pwm_trace_uart_tb.v 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. `timescale 1ns/1ps
  2. module pwm_trace_uart_tb;
  3. localparam integer CLOCK_HZ = 25_000_000;
  4. localparam integer TEST_BAUD = 1_000_000;
  5. localparam integer BIT_NS = 1000;
  6. reg sys_clk = 1'b0;
  7. reg reset_n = 1'b0;
  8. reg trace_heater = 1'b0;
  9. reg trace_fan = 1'b0;
  10. reg trace_motor1 = 1'b0;
  11. reg trace_motor2 = 1'b0;
  12. reg trace_motor3 = 1'b0;
  13. reg trace_limit1 = 1'b0;
  14. reg trace_limit2 = 1'b0;
  15. reg trace_limit3 = 1'b0;
  16. wire uart_tx;
  17. wire led_activity_n;
  18. integer errors = 0;
  19. integer i;
  20. reg [7:0] packet [0:15];
  21. reg [15:0] event_id;
  22. reg [31:0] data_value;
  23. reg [31:0] packet_time;
  24. reg [31:0] first_timestamp;
  25. reg [31:0] heater_count, heater_period, heater_high;
  26. reg [31:0] fan_count, fan_period, fan_high;
  27. reg [31:0] motor1_count, motor1_period, motor1_high;
  28. reg [31:0] motor2_count, motor2_period, motor2_high;
  29. reg [31:0] motor3_count, motor3_period, motor3_high;
  30. reg [31:0] status_data;
  31. always #20 sys_clk = ~sys_clk;
  32. pwm_trace_top #(
  33. .CLOCK_HZ(CLOCK_HZ),
  34. .BAUD(TEST_BAUD),
  35. .REPORT_MS(20)
  36. ) dut (
  37. .sys_clk(sys_clk),
  38. .reset_n(reset_n),
  39. .trace_heater(trace_heater),
  40. .trace_fan(trace_fan),
  41. .trace_motor1(trace_motor1),
  42. .trace_motor2(trace_motor2),
  43. .trace_motor3(trace_motor3),
  44. .trace_limit1(trace_limit1),
  45. .trace_limit2(trace_limit2),
  46. .trace_limit3(trace_limit3),
  47. .uart_tx(uart_tx),
  48. .led_activity_n(led_activity_n)
  49. );
  50. // Distinct waveforms verify that each event ID is tied to the intended
  51. // channel rather than accidentally reusing another channel's measurement.
  52. initial forever begin
  53. @(negedge sys_clk); trace_heater = 1'b1;
  54. repeat (250) @(negedge sys_clk); trace_heater = 1'b0;
  55. repeat (749) @(negedge sys_clk);
  56. end
  57. initial forever begin
  58. @(negedge sys_clk); trace_fan = 1'b1;
  59. repeat (500) @(negedge sys_clk); trace_fan = 1'b0;
  60. repeat (749) @(negedge sys_clk);
  61. end
  62. initial forever begin
  63. @(negedge sys_clk); trace_motor1 = 1'b1;
  64. repeat (1250) @(negedge sys_clk); trace_motor1 = 1'b0;
  65. repeat (1249) @(negedge sys_clk);
  66. end
  67. initial forever begin
  68. @(negedge sys_clk); trace_motor2 = 1'b1;
  69. repeat (1000) @(negedge sys_clk); trace_motor2 = 1'b0;
  70. repeat (3999) @(negedge sys_clk);
  71. end
  72. initial forever begin
  73. @(negedge sys_clk); trace_motor3 = 1'b1;
  74. repeat (7500) @(negedge sys_clk); trace_motor3 = 1'b0;
  75. repeat (2499) @(negedge sys_clk);
  76. end
  77. // Limit transitions before the first 20 ms report:
  78. // current raw state at snapshot: L3=1, L2=1, L1=0 -> 3'b110
  79. // rising seen: all three; falling seen: L1 only.
  80. initial begin
  81. wait(reset_n == 1'b1);
  82. #2000000; trace_limit1 = 1'b1;
  83. #2000000; trace_limit2 = 1'b1;
  84. #2000000; trace_limit1 = 1'b0;
  85. #2000000; trace_limit3 = 1'b1;
  86. end
  87. function [7:0] crc8_next;
  88. input [7:0] crc_in;
  89. input [7:0] data_in;
  90. integer k;
  91. reg [7:0] c;
  92. begin
  93. c = crc_in ^ data_in;
  94. for (k = 0; k < 8; k = k + 1) begin
  95. if (c[7])
  96. c = (c << 1) ^ 8'h07;
  97. else
  98. c = (c << 1);
  99. end
  100. crc8_next = c;
  101. end
  102. endfunction
  103. task uart_recv_byte;
  104. output [7:0] value;
  105. integer b;
  106. begin
  107. @(negedge uart_tx);
  108. #(BIT_NS + BIT_NS/2);
  109. for (b = 0; b < 8; b = b + 1) begin
  110. value[b] = uart_tx;
  111. #BIT_NS;
  112. end
  113. if (uart_tx !== 1'b1) begin
  114. $display("FAIL UART stop bit");
  115. errors = errors + 1;
  116. end
  117. #(BIT_NS/2);
  118. end
  119. endtask
  120. task recv_packet;
  121. integer n;
  122. reg [7:0] crc;
  123. begin
  124. packet[0] = 8'h00;
  125. packet[1] = 8'h00;
  126. while (!((packet[0] == 8'hA5) && (packet[1] == 8'h5A))) begin
  127. packet[0] = packet[1];
  128. uart_recv_byte(packet[1]);
  129. end
  130. for (n = 2; n < 16; n = n + 1)
  131. uart_recv_byte(packet[n]);
  132. crc = 8'h00;
  133. for (n = 2; n <= 14; n = n + 1)
  134. crc = crc8_next(crc, packet[n]);
  135. if (crc !== packet[15]) begin
  136. $display("FAIL CRC expected=%02x actual=%02x", crc, packet[15]);
  137. errors = errors + 1;
  138. end
  139. if (packet[2] !== 8'h82) begin
  140. $display("FAIL packet type expected=82 actual=%02x", packet[2]);
  141. errors = errors + 1;
  142. end
  143. event_id = {packet[5], packet[6]};
  144. packet_time = {packet[7], packet[8], packet[9], packet[10]};
  145. data_value = {packet[11], packet[12], packet[13], packet[14]};
  146. end
  147. endtask
  148. task check_u32;
  149. input [8*24-1:0] label_text;
  150. input [31:0] actual;
  151. input [31:0] expected;
  152. begin
  153. if (actual !== expected) begin
  154. $display("FAIL %0s expected=%0d actual=%0d", label_text, expected, actual);
  155. errors = errors + 1;
  156. end else begin
  157. $display("PASS %0s = %0d", label_text, actual);
  158. end
  159. end
  160. endtask
  161. initial begin
  162. $display("");
  163. $display("==============================================");
  164. $display(" 8-CHANNEL TRACE UART INTEGRATION TEST");
  165. $display("==============================================");
  166. repeat (10) @(posedge sys_clk);
  167. reset_n = 1'b1;
  168. heater_count = 0; heater_period = 0; heater_high = 0;
  169. fan_count = 0; fan_period = 0; fan_high = 0;
  170. motor1_count = 0; motor1_period = 0; motor1_high = 0;
  171. motor2_count = 0; motor2_period = 0; motor2_high = 0;
  172. motor3_count = 0; motor3_period = 0; motor3_high = 0;
  173. status_data = 0;
  174. first_timestamp = 32'hFFFF_FFFF;
  175. for (i = 0; i < 16; i = i + 1) begin
  176. recv_packet();
  177. $display("TRACE event=0x%04x timestamp=%0d data=%0d", event_id, packet_time, data_value);
  178. if (i == 0)
  179. first_timestamp = packet_time;
  180. else if (packet_time !== first_timestamp) begin
  181. $display("FAIL snapshot timestamps differ");
  182. errors = errors + 1;
  183. end
  184. case (event_id)
  185. 16'h5101: heater_count = data_value;
  186. 16'h5102: heater_period = data_value;
  187. 16'h5103: heater_high = data_value;
  188. 16'h5111: fan_count = data_value;
  189. 16'h5112: fan_period = data_value;
  190. 16'h5113: fan_high = data_value;
  191. 16'h5121: motor1_count = data_value;
  192. 16'h5122: motor1_period = data_value;
  193. 16'h5123: motor1_high = data_value;
  194. 16'h5131: motor2_count = data_value;
  195. 16'h5132: motor2_period = data_value;
  196. 16'h5133: motor2_high = data_value;
  197. 16'h5141: motor3_count = data_value;
  198. 16'h5142: motor3_period = data_value;
  199. 16'h5143: motor3_high = data_value;
  200. 16'h5200: status_data = data_value;
  201. default: begin
  202. $display("FAIL unexpected event ID 0x%04x", event_id);
  203. errors = errors + 1;
  204. end
  205. endcase
  206. end
  207. if (heater_count == 0 || fan_count == 0 || motor1_count == 0 || motor2_count == 0 || motor3_count == 0) begin
  208. $display("FAIL one or more pulse counts are zero");
  209. errors = errors + 1;
  210. end else begin
  211. $display("PASS all five pulse counts > 0");
  212. end
  213. check_u32("heater_period", heater_period, 32'd1000);
  214. check_u32("heater_high", heater_high, 32'd250);
  215. check_u32("fan_period", fan_period, 32'd1250);
  216. check_u32("fan_high", fan_high, 32'd500);
  217. check_u32("motor1_period", motor1_period, 32'd2500);
  218. check_u32("motor1_high", motor1_high, 32'd1250);
  219. check_u32("motor2_period", motor2_period, 32'd5000);
  220. check_u32("motor2_high", motor2_high, 32'd1000);
  221. check_u32("motor3_period", motor3_period, 32'd10000);
  222. check_u32("motor3_high", motor3_high, 32'd7500);
  223. // Status bit layout check. PWM instantaneous level bits [4:0] are not
  224. // checked because the report can occur at any phase of each waveform.
  225. if (status_data[7:5] !== 3'b110) begin
  226. $display("FAIL limit levels expected=110 actual=%b", status_data[7:5]);
  227. errors = errors + 1;
  228. end else begin
  229. $display("PASS limit raw levels = 110");
  230. end
  231. if (status_data[12:8] !== 5'b11111) begin
  232. $display("FAIL valid flags expected=11111 actual=%b", status_data[12:8]);
  233. errors = errors + 1;
  234. end
  235. if (status_data[17:13] !== 5'b11111) begin
  236. $display("FAIL present flags expected=11111 actual=%b", status_data[17:13]);
  237. errors = errors + 1;
  238. end
  239. if (status_data[20:18] !== 3'b111) begin
  240. $display("FAIL limit rise-seen expected=111 actual=%b", status_data[20:18]);
  241. errors = errors + 1;
  242. end
  243. if (status_data[23:21] !== 3'b001) begin
  244. $display("FAIL limit fall-seen expected=001 actual=%b", status_data[23:21]);
  245. errors = errors + 1;
  246. end
  247. $display("");
  248. $display("==============================================");
  249. if (errors == 0) begin
  250. $display("ALL 8-CHANNEL TRACE UART TESTS PASSED");
  251. $display("==============================================");
  252. $finish;
  253. end else begin
  254. $display("8-CHANNEL TRACE UART TEST FAILED: %0d error(s)", errors);
  255. $display("==============================================");
  256. $fatal(1);
  257. end
  258. end
  259. endmodule