// Solution to LSU EE 4702-1 Spring 2000 Homework 3. `timescale 1us/1us `define timeunit 1000000 `define bits 10 `define vsize (`bits-1):0 `define longtime 10 // Behavioral description of master-slave edge triggered register. module DREGx(q,d,c); output q; input d,c; reg [`vsize] q, nextq; wire [`vsize] d; wire c; initial begin q = 0; nextq = 0; end always @( posedge c ) nextq <= d; always @( negedge c ) q <= nextq; endmodule // DREGx // Behavioral description of master-slave edge triggered flip-flop. module DREG1(q,d,c); output q; input d,c; reg q, nextq; wire d; wire c; initial begin q = 0; nextq = 0; end always @( posedge c ) nextq <= d; always @( negedge c ) q <= nextq; endmodule // DREG1 module tach1s(rpx,pd,clk); input pd, clk; output rpx; wire pd, clk; wire [`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. parameter precision = perwhat / ( marks * update_interval ); parameter timer_ticks = update_interval * freq - 1; integer timer_ticks_i, precision_i; initial begin timer_ticks_i = timer_ticks; precision_i = precision; end wire reset; wire [`vsize] count, update_timer; wire [`vsize] next_count = reset ? 0 : count + precision_i; DREGx count_reg(count,next_count,pd); wire reset_acknowledged = reset && count === 0; wire End_of_interval = !reset && update_timer === 0; wire [`vsize] next_update_timer = reset_acknowledged ? timer_ticks_i : End_of_interval ? 0 : update_timer - 1; DREGx update_timer_reg(update_timer,next_update_timer,clk); wire next_reset = reset_acknowledged ? 0 : End_of_interval ? 1 : reset; DREG1 reset_reg(reset,next_reset,clk); // Clocking on reset instead of clk ensures that count is based // on a full timer_ticks cylces, not timer_ticks - 0.5 cycles. DREGx rpx_reg(rpx,count,reset); endmodule // tach1s module tach2(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 perwhat = 60; // Measure in revolutions per 60 seconds. parameter one_cycle_rpx = freq * perwhat / marks; parameter max_count = ( (1<<`bits) - 1 ); parameter min_count = 1 + one_cycle_rpx / max_count; reg [`vsize] count; reg bit, last_bit; initial begin:INIT count = max_count; bit = 0; rpx = 0; last_bit = 0; end // block: INIT always @( posedge pd ) bit <= ~bit; always @( posedge clk ) begin last_bit <= bit; casez ( {last_bit !== bit, count === max_count } ) 2'b10: begin rpx <= count < min_count ? max_count : one_cycle_rpx / count; count <= 1; end 2'b11: begin rpx <= 0; count <= 1; end 2'b00: count <= count + 1; 2'b01: begin rpx <= 0; count <= count; end endcase // casez( {last_bit !== bit, count === max_count } ) end // always @ ( posedge clk ) endmodule // tach2 //`define FAST `ifdef FAST // Test fast. module tt(); wire done; // Test at default values. tta t1(done,1'b1); initial begin #1; wait( done === 1 ); $stop; end endmodule // tt `else // !ifdef FAST module tt(); wire d1,d2,d3,d4,d5; // Test at default values. tta t1(d1,1'b1); tta #(100) t2(d2,d1); tta #(500,20) t3(d3,d2); tta #(1000) t4(d4,d3); tta #(2000,4,2) t5(d5,d4); initial begin wait( d5 === 1 ); $stop; end endmodule // tt `endif // !ifdef FAST // Utility functions for tt2a. module Util(); function integer min; input a,b; integer a,b; min = a < b ? a : b; endfunction // min function integer max; input a,b; integer a,b; max = a > b ? a : b; endfunction // min endmodule // util // Test Tach 2a module tta(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_pw = 60; tach2 #(p_fr,p_ma,p_pw) s1(rpx,pd,clk); // Dummy module defining min and max functions. Util util(); initial begin:I 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_done; // 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 and check_high_speed; integer prev_check_low_speed, prev_check_high_speed; // Number of errors during and after adjustment period. integer adjust_error, check_error; // Number of speeds tested at. integer tests; // Lowest non-stationary speed that can be measured. integer lower_limit; // Highest non-saturating speed that can be measured. integer upper_limit; integer i; integer max_number; done = 0; wait( start === 1 ); $display("Starting: f %d, m %d, per %d secs\n", s1.freq, s1.marks, s1.perwhat); clk = 0; tests = 0; adjust_error = 0; check_error = 0; markratio = 0.7; markcount = s1.marks; // Assume tach initially at zero. check_low_speed = 0; max_number = (1<<`bits) - 1; // Low speed based on size of count register. lower_limit = util.max(1,s1.one_cycle_rpx / max_number); // High speed based on size of output and clock frequency. upper_limit = util.min(max_number,s1.one_cycle_rpx); // Iterate through three types of tests. (i negative, zero, positive) for(i=-5; i<5; i=i+1) begin:SPEEDLOOP integer count; integer update_interval; case( 1 ) // Stationary. i == 0: speed_rpx = 0; // High speeds. i < 0: speed_rpx = - 2 * upper_limit / i; // Low speeds. i > 0: speed_rpx = lower_limit + i; endcase // case( 1 ) tests = tests + 1; speed_rps = 1.0 * speed_rpx / s1.perwhat; // Compute amount of time mark under photodetector... markon = speed_rps == 0 ? 10 * `timeunit * max_number / s1.freq : 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 = speed_rps == 0 ? 0 : 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 )); // The number of cycles between marks. count = speed_rps == 0 ? max_number : util.min(max_number,s1.one_cycle_rpx / true_rpx); prev_check_low_speed = check_low_speed; prev_check_high_speed = check_high_speed; check_low_speed = count == max_number ? 0 : util.min(max_number, s1.one_cycle_rpx / ( count + 1 )); check_high_speed = util.min(max_number, s1.one_cycle_rpx / util.max(1,count-1)); adjust_low_speed = util.min(prev_check_low_speed,check_low_speed); adjust_high_speed = util.max(prev_check_high_speed,check_high_speed); update_interval = util.min( `timeunit * max_number / s1.freq, markon + markoff ); // Time at which a correct speed is expected. adjust_time = $time + 2 * update_interval; // Amount of time to test this speed. test_done = $time + update_interval * 4.3; // fork, not begin! fork:CHECKSPEED // Simulate photodetectors and exit loop when done. // Exit just before a new photodetector starts so that // next time loop entered tachometer will see new speed // instead of an transient speed that might be slower or faster, // as would happen in test bench for hw02. begin while( $time < test_done ) begin pd <= 1; # markon; pd <= 0; # markoff; end disable CHECKSPEED; 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 // 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 // Clock. always begin:CLOCK wait( start === 1 && done === 0 ); forever # (`timeunit * 0.5/s1.freq ) clk <= ~ clk; end endmodule // test_speed