```// 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.
// 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 errors during and after adjustment period.
// 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));

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
( rpx === `bits'bx ||
( 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
( rpx === `bits'bx ||
( 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",
\$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

```