// // Template for solution to LSU EE 4702-1 Spring 2000 Homework 3. // // Name: // Instructions: // // Copy this file to a file named hw03sol.v in your class directory, // and use it for your solution. There is no need to add the homework-2 // testbench to this file. (But it's okay if you do.) // // Use the module names provided in this file. Other modules can be // added, but use the names provided for the tachometers. // // The files will be copied from the class accounts on Friday, the // time will be announced. `timescale 1us/1us `define timeunit 1000000 `define bits 10 `define vsize (`bits-1):0 //////////////////////////////////////////////////////////////////////////////// // // Code for Problem 1 // // Master slave registers are included below for convenience, others // may be added. // Use the template below for your solution. The template includes // a parameter bug workaround: parameter values are assigned to // integers. These integers can be used in the description, but // they cannot be changed. (If a parameter is the result of evaluating // an expression containing floating-point numbers it will be considered // floating-point (even if it's an integer) and will evaluate to zero // in integer expressions. // 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; // Bug workaround. No other procedural code should be used. initial begin timer_ticks_i = timer_ticks; precision_i = precision; end // Insert solution here. endmodule // tach1s //////////////////////////////////////////////////////////////////////////////// // // Code for Problem 2 // // Use the template provided below. // The output and internal timers must all use `bits-bit registers. // (Use the `vsize and `bits macros). The testbench bases its test // speeds and tolerances on these register sizes. // The testbench follows the template. module tach2(rpx,pd,clk); input pd, clk; output rpx; wire pd, clk; reg [`vsize] rpx; // Parameters intended for instantiation. parameter freq = 500; parameter marks = 4; // Four pulses per revolution. parameter perwhat = 60; // Measure in revolutions per 60 seconds. // Parameters that might be useful in the module. 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; // Insert solution here. endmodule // tach2 // // Problem 2 testbench code. // // Define fast to run the fast test, undefine it for the detailed test. // Module names have been made shorter so that the signal names will // be visible in the default wave window, and for easier typing. `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