// Solution Template for LSU EE 4702-1 Spring 2000 Homework 2 // Name: `timescale 1us/1us `define timeunit 1000000 `define bits 10 `define vsize (`bits-1):0 module tach1(rpx,pd,clk); input pd, clk; output rpx; wire pd, clk; reg [`vsize] rpx; parameter freq = 500; parameter marks = 4; // Four pulses per revolution. parameter update_interval = 0.5; // Update every update_interval seconds. parameter perwhat = 60; // Measure in revolutions per 60 seconds. reg myclock; always #1 myclock = ~myclock; // Solution goes here. endmodule // Test one instance. Use for initial debugging. module test_tach1_fast(); wire done; // Test at default values. test_tach1a ts1(done,1'b1); // Module should complain about parameters below: // test_tach1a #(500,17) ts2(done2,1'b1); initial begin wait( done === 1 ); $stop; end endmodule // test_tach1_fast // Test several instances. For when you're almost done. module test_tach1_detailed(); wire d1,d2,d3,d4,d5; // Test at default values. test_tach1a ts1(d1,1'b1); test_tach1a #(100) ts2(d2,d1); test_tach1a #(500,20) ts3(d3,d2); test_tach1a #(500,3,2) ts4(d4,d3); test_tach1a #(500,4,0.5,2) ts5(d5,d4); initial begin wait( d5 === 1 ); $stop; end endmodule // test_tach1_fast module test_tach1a(done,start); output done; input start; wire start; reg done; wire [`vsize] rpx; reg pd, clk; parameter p_fr = 500; parameter p_ma = 4; parameter p_ui = 0.5; parameter p_pw = 60; tach1 #(p_fr,p_ma,p_ui,p_pw) s1(rpx,pd,clk); initial begin:IBLOCK integer markcount; // Number of marks per revolution. real markratio; // Fraction of circumference covered by marks. // Amount of time given by testbench to detect new speed. realtime adjust_time; // Amount of time given by testbench to test a speed. realtime test_duration; // Time (scaled) between speed updates. realtime update_interval; // Speed testbench would like to test at. integer speed_rpx; // Units: revolutions per s1.perwhat seconds. // Speed testbench actually testing at. (Due to rounding errors.) real true_rpx; // Speed in revolutions per second. real speed_rps; // Amount of time photodetector is on, and off (as mark passes under). time markon, markoff; // Low and high range of correct speeds while adjusting. integer adjust_low_speed, adjust_high_speed; // Low and high range of correct speed after adjusting. integer check_low_speed, check_high_speed; // Previous value of check_low_speed. integer old_check_low_speed; // Number of errors during and after adjustment period. integer adjust_error, check_error; // Tolerance of measured speed. (E.g., +/- 20 RPM.) integer precision; // Number of speeds tested at. integer tests; integer loop_increment; done = 0; wait( start === 1 ); $display("Starting: f %d, m %d, interv %f, per %d secs\n", s1.freq, s1.marks,s1.update_interval,s1.perwhat); clk = 0; tests = 0; adjust_error = 0; check_error = 0; markratio = 0.7; markcount = s1.marks; update_interval = `timeunit * s1.update_interval; precision = s1.perwhat / ( s1.marks * s1.update_interval ); // Assume tach initially at zero. check_low_speed = 0; // Iterate over about 24 tests, fastest speed chosen // to avoid overflow of counters, just barely. loop_increment = ( 1 << (`bits-3) ) - ( precision >> 3 ) - 1; for(speed_rpx = 0; speed_rpx <= (1<<`bits) - precision; speed_rpx = speed_rpx + loop_increment) begin:SPEEDLOOP tests = tests + 1; speed_rps = speed_rpx / s1.perwhat; // Compute amount of time mark under photodetector... markon = 1 + `timeunit * ( markratio / markcount ) / speed_rps; // ...and amount of time in gap between marks. // Add 1 so speed is rounded to a lower rather than higher val. markoff = 1 + `timeunit * ( (1-markratio) / markcount ) / speed_rps; // Compute the actual speed, which is different than // speed_rpx because markon and markoff are integers. true_rpx = 1.0 * s1.perwhat * `timeunit / (markcount * ( markon + markoff )); old_check_low_speed = check_low_speed; check_low_speed = true_rpx <= precision ? 0 : true_rpx - precision; check_high_speed = true_rpx + precision; adjust_low_speed = old_check_low_speed; adjust_high_speed = check_high_speed; // Time at which a correct speed is expected. adjust_time = $time + update_interval * 2; // Amount of time to test this speed. test_duration = update_interval * 4.3; // fork, not begin! fork:CHECKSPEED // Note that line below stops the "forevers" # test_duration disable CHECKSPEED; // Simulate photodetectors. forever begin pd <= 1; # markon; pd <= 0; # markoff; end // Check tach whenever its output changes. forever @( rpx ) begin if( $time < adjust_time && ( rpx === `bits'bx || rpx < adjust_low_speed || rpx > adjust_high_speed ) ) adjust_error = adjust_error + 1; if( $time > adjust_time && ( rpx === `bits'bx || rpx < check_low_speed || rpx > check_high_speed ) ) check_error = check_error + 1; end // Check tach at fixed intervals. forever # update_interval begin if( $time < adjust_time && ( rpx === `bits'bx || rpx < adjust_low_speed || rpx > adjust_high_speed ) ) adjust_error = adjust_error + 1; if( $time > adjust_time && ( rpx === `bits'bx || rpx < check_low_speed || rpx > check_high_speed ) ) check_error = check_error + 1; end join end // for (speed_rpx = 0; speed_rpx < 'h10000;... // Done with all tests, report results. $display("Finished %d tests with %d adjust errors and %d check errors.\n", tests,adjust_error,check_error); if( check_error || adjust_error ) $display("*** ERRORS FOUND ***\n"); done = 1; // Stop the clock. (Simulator efficiency.) disable CLOCK; end // block: IBLOCK // Clock. always begin:CLOCK wait( start === 1 && done === 0 ); forever # (`timeunit * 0.5/s1.freq ) clk <= ~ clk; end endmodule // test_speed