```// Solution to LSU EE 4702-1 Spring 2000 Homework 3.

`timescale 1us/1us
`define timeunit 1000000

`define bits 10
`define vsize (`bits-1):0

module tach3(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;
parameter  mark_min = ( marks + 2 ) / 2; // Minimum no. of marks per rev.

reg [`vsize] count;
reg [4:0]    marks_to_go;
reg [`vsize] low_count, next_low_count;
reg          bit, last_bit;
reg          new_mark, overflow;
reg          first_mark, last_mark, at_overflow;

function [`vsize] compute_rpx;
input [`vsize] low_count;
if( low_count == max_count )
compute_rpx = 0;
else if( low_count < min_count )
compute_rpx = max_count;
else
compute_rpx = one_cycle_rpx / low_count;
endfunction // compute_rpx

initial
begin:INIT
marks_to_go = 0;
overflow = 0;
at_overflow = 0;
count = max_count;
low_count = max_count;
new_mark = 0;
bit = 0;
rpx = 0;
last_bit = 0;
end // block: INIT

always @( posedge pd ) bit <= ~bit;

always @( posedge clk ) begin

new_mark    = last_bit !== bit;
first_mark  = marks_to_go === marks - 1;
last_mark   = marks_to_go === 0;
at_overflow = count === max_count;

if ( new_mark ) begin

count <= 1;

casez( { overflow, first_mark, count < low_count } )
3'b11?:   next_low_count = max_count;
3'b0?1:   next_low_count = count;
default:  next_low_count = low_count;
endcase

end else begin

count <= count + 1;
next_low_count = low_count;

end // else: !if( new_mark )

if( at_overflow || new_mark )
if( last_mark ) begin

rpx           <= compute_rpx(next_low_count);
next_low_count = max_count;
marks_to_go   <= marks - 1;

end else begin

marks_to_go   <= marks_to_go - 1;

end // else: !if( last_mark )

low_count <= next_low_count;

overflow <= at_overflow ? 1 :
new_mark    ? 0 : overflow;

last_bit <= bit;

end // always @ ( negedge clk )

endmodule // tach3

`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,15) t2(d2,d1);
tta #(500,20) t3(d3,d2);
tta #(1000,17) t4(d4,d3);
tta #(2000,3,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

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;

tach3  #(p_fr,p_ma,p_pw) s1(rpx,pd,clk);

// Number of errors during and after adjustment period.

// Dummy module defining min and max functions.
Util util();

parameter   pi_reset = 0;
parameter   pi_new_pattern = 1;
parameter   pi_next_mark = 2;

function integer pattern_iterator;
input [1:0] operation;
integer loc;
integer pnum;
integer pattern[0:100]; // Assumed maximum number of marks.
integer i;
integer mark_min;
integer missing_max;

if( operation === pi_next_mark )
begin
pattern_iterator = pattern[loc];
loc = loc === s1.marks - 1 ? 0 : loc + 1;
if( s1.marks > 101 )
begin
\$display("pattern_iterator can only handle up to 101 marks.");
\$stop;
end
end
else
begin
if( operation === pi_reset )
begin
loc = 0;
pnum = 0;
end
else if ( operation !== pi_new_pattern )
begin
\$display("Unexpected input to pattern iterator.");
\$stop;
end

pnum = pnum + 1;
mark_min = s1.mark_min;
missing_max = s1.marks - mark_min;
for(i=0; i<s1.marks; i=i+1) pattern[i] = 0;
case( pnum )
1: // All marks present.
for(i=0; i<s1.marks; i=i+1) pattern[i] = 1;
2: // Alternate marks present.
begin
for(i=0; i<s1.marks; i=i+2) pattern[i] = 1;
if( ( s1.marks & 1 ) === 0 ) pattern[1] = 1;
end
3: // Maximum gap.
for(i=0; i<mark_min; i=i+1) pattern[i] = 1;
4: // Half-size gap.
for(i=0; i<s1.marks - missing_max/2 ; i=i+1) pattern[i] = 1;
5: // Random
begin
for(i=0; i<mark_min; i=i+1) pattern[i] = 1;
for(i=0; i<s1.marks; i=i+1)
begin:R
integer to, temp;
to = (\$random>>1) % s1.marks;
temp = pattern[i]; pattern[i] = pattern[to];
pattern[to] = temp;
end
end // case: 5
default: pnum = 0;
endcase // case( pnum )
pattern_iterator = pnum;
end
endfunction

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.
// 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.
// 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 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;
// Pattern number in use.
integer  patnum;
// Speeds to test tachometer at.
integer  speeds[0:10];
// Any speed lower than this will be considered slow.
integer  low_speed_threshold;

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; low_speed_error = 0;

markratio = 0.7;
markcount = s1.marks;

max_number = (1<<`bits) - 1;

// Assume tach initially at zero.
check_low_speed = 0;
prev_check_high_speed = max_number;
check_high_speed = max_number;

// 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 patterns. Iterator returns zero when done.
for( patnum = pattern_iterator( pi_reset );
patnum;
patnum = pattern_iterator( pi_new_pattern ) )
begin

tests = 0;
adjust_error = 0; check_error = 0; low_speed_error = 0;

// Iterate through 3 types of tests. (i negative, zero, positive),
// assign speeds to an array.
for( i = 0; i < 5; i = i + 1 ) begin
// High speeds.
speeds[2*i] = 2 * upper_limit / ( i + 1 );
// Low speeds.
speeds[2*i+1] = lower_limit + i + 1;
end

low_speed_threshold = speeds[9] + 1;
speeds[10] = 0;

// Mix up speeds.
for(i=0; i<11; i=i+1) begin:A
integer r, temp;
r = ( \$random >> 1 ) % 11;
temp = speeds[i]; speeds[i] = speeds[r]; speeds[r] = temp;
end

// Iterate through 3 types of tests. (i negative, zero, positive)
for(i=0; i<11; i=i+1) begin:SPEEDLOOP
integer count;
integer update_interval;

tests = tests + 1;

speed_rpx = speeds[i];

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));

check_low_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 + 4 * s1.marks * update_interval;
// Amount of time to test this speed.
test_done = adjust_time + 3 * s1.marks * update_interval;

// 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
if( pattern_iterator( pi_next_mark ) ) pd <= 1;
# markon;
if( markoff ) begin pd <= 0; # markoff; end
end
disable CHECKSPEED;
end

// Check tach whenever its output changes.
forever @( rpx )
begin
!== 1 )
begin
if( true_rpx < low_speed_threshold )
low_speed_error = low_speed_error + 1;
else
end

( rpx >= check_low_speed && rpx <= check_high_speed )
!== 1 )
begin
if( true_rpx < low_speed_threshold )
low_speed_error = low_speed_error + 1;
else
check_error = check_error + 1;
end
end

// Check tach at fixed intervals.
forever # update_interval
begin
!== 1 )
begin
if( true_rpx < low_speed_threshold )
low_speed_error = low_speed_error + 1;
else
end

( rpx >= check_low_speed && rpx <= check_high_speed )
!== 1 )
begin
if( true_rpx < low_speed_threshold )
low_speed_error = low_speed_error + 1;
else
check_error = check_error + 1;
end
end
join

end // block: SPEEDLOOP

// Done with all speeds for pattern, report results.

\$display("Pattern %1d,  %2d speeds. Errors: %3d adjust, %3d check, and %3d low-speed\n",
\$display("OUTCOME: %s  With %2d Marks, High Speed Pattern %1d",
( adjust_error || check_error ) ? "FAIL" : "PASS",
s1.marks, patnum);
\$display("OUTCOME: %s  With %2d Marks, Low Speed Pattern %1d",
( low_speed_error ) ? "FAIL" : "PASS",
s1.marks, patnum);

end // for ( patnum = pattern_iterator( pi_reset );...

done = 1;

// Stop the clock. (Simulator efficiency.)
disable CLOCK;

end // block: I

// Clock.
always
begin:CLOCK
wait( start === 1 && done === 0 );
forever # (`timeunit * 0.5/s1.freq ) clk <= ~ clk;
end

always
forever @( check_error or adjust_error )
if( check_error > 0 || adjust_error > 0 ) \$stop;

endmodule // test_speed

```