////////////////////////////////////////////////////////////////////////////////
///
/// Template for LSU EE 4702-1 Spring 2001 Homework 4
///
/// Name:
/// Instructions:
//
// Copy this to a file named hw04sol.v to directory ~/hw in your
// class account. (~ is your home directory.) Use this
// file for your solution. Your entire solution should be in
// this file.
//
// Do not rename the modules in this file and be sure to use the
// directory and filename given above.
// Assignment: http://www.ee.lsu.edu/v/2001/hw04.pdf
////////////////////////////////////////////////////////////////////////////////
`timescale 1us/1us
module microwave_oven_controller(beep,dmt,dmu,dst,dsu,mag_on,
key_code,reset,clk);
input key_code; // Key begin pressed (see parameters).
// Can be tested on the positive edge of clk.
input reset; // Reset signal. Can be tested on posedge clk.
input clk; // A 64 Hz clock. (Did Westinghouse consider it?)
output mag_on; // When 1, magnetron is on (oven is heating).
output beep; // When 1, emit tone.
output dmt; // Tens digit of minute display.
output dmu; // Units digit of minute display.
output dst, dsu; // Tens and units digits of seconds display.
wire clk;
wire [5:0] key_code;
reg [3:0] dmt, dmu, dst, dsu;
reg mag_on;
// Do not modify key numbers.
parameter key_none = 6'd0; // No key pressed.
parameter key_never = 6'd1; // This code will never be returned.
parameter key_start = 6'd10;
parameter key_reset = 6'd11;
parameter key_power = 6'd12;
parameter key_0 = 6'd20;
parameter key_1 = 6'd21;
parameter key_2 = 6'd22;
parameter key_3 = 6'd23;
parameter key_4 = 6'd24;
parameter key_5 = 6'd25;
parameter key_6 = 6'd26;
parameter key_7 = 6'd27;
parameter key_8 = 6'd28;
parameter key_9 = 6'd29;
endmodule // microwave_oven_controller
// exemplar translate_off
module test_oven();
reg clk;
wire [3:0] dmt, dmu, dst, dsu;
wire mag_on;
reg [5:0] key_mod;
reg reset;
// Set this to 1 to have each change in the oven display appear
// on the console.
reg monitor_display;
// Set this to one to have each key press appear on the console.
reg monitor_keys;
reg monitor_beep;
reg monitor_mag;
// Set this to one to get long test.
reg patient;
microwave_oven_controller oven(beep,dmt,dmu,dst,dsu,mag_on,key_mod,reset,clk);
time tics;
wire [15:0] mod_digits = {dmt,dmu,dst,dsu};
initial tics = 0;
always begin clk = 0; #5625; tics = tics + 1; #0; clk=1; #10000; end
parameter ss_reset = 3'd0;
parameter ss_digit1 = 3'd1; // Single digit, power not entered.
parameter ss_digit2 = 3'd2; // Power entered or > 1 digit.
parameter ss_cook = 3'd3;
parameter ss_pause = 3'd4;
parameter kty_digit = 6'd30;
reg [15:0] shadow_display, alt_display;
integer alt_disp_stale;
reg [2:0] shadow_state;
integer shadow_secs, mod_secs, delta;
integer shadow_tics, pause_tics, start_tics;
integer shadow_power, shadow_digits;
integer expected_beep_done, expecting_done_beep;
integer watch_display;
reg [7:0] error_display, error_mag, error_beep;
integer error_total;
reg [7:0] error_beep_total, error_mag_total, error_display_total;
parameter key_none = 6'd0; // No key pressed.
parameter key_never = 6'd1; // This code will never be returned.
parameter key_start = 6'd10;
parameter key_reset = 6'd11;
parameter key_power = 6'd12;
parameter key_0 = 6'd20;
parameter key_1 = 6'd21;
parameter key_2 = 6'd22;
parameter key_3 = 6'd23;
parameter key_4 = 6'd24;
parameter key_5 = 6'd25;
parameter key_6 = 6'd26;
parameter key_7 = 6'd27;
parameter key_8 = 6'd28;
parameter key_9 = 6'd29;
parameter show_key = 1;
function integer abs;
input a;
integer a;
abs = a < 0 ? -a : a;
endfunction // abs
`define check_digit(d,l) \
if( (d) > (l) || (d) < 0 ) begin \
error_display = error_display | 1; \
secs = -1; \
disable tosecs; \
end
// Convert time on display to seconds.
//
task tosecs;
output secs;
integer secs;
begin
`check_digit(dmt,9)
`check_digit(dmu,9)
`check_digit(dst,5)
`check_digit(dsu,9)
secs = dmt * 600 + dmu * 60 + dst * 10 + dsu;
end
endtask // tosecs
/// Listen Beep
//
always @( beep )
if( beep ) begin
if( monitor_beep ) $display("Beep starting.");
if( expecting_done_beep )
begin
expected_beep_done = 128;
expecting_done_beep = 0;
end
if( expected_beep_done == 0 ) begin
$display("Should not be beeping.");
error_beep = error_beep | 1;
end
end else begin:B // if ( beep )
integer delta;
if( monitor_beep ) $display("Beep ending.");
delta = abs( tics - expected_beep_done );
if( shadow_state != ss_reset
&& expected_beep_done && delta > 5 ) begin
$display("Beep wrong time. %d",delta);
error_beep = error_beep | 2;
end
expected_beep_done = 0;
end
/// Watch Magnetron
//
integer mag_on_start, mag_on_total;
always @( mag_on )
if( mag_on ) begin
if( monitor_mag ) $display("Mag on.");
if( shadow_state != ss_cook )
begin
$display("Mag on when cooking off.");
error_mag = error_mag | 1;
end
if( mag_on_start != 0 ) begin:A
integer cycle_this;
cycle_this = tics - mag_on_start;
if( shadow_power > 0 && shadow_power < 10
&& abs( cycle_this - 160 ) > 10 )
begin
$display("Mag cycle error.");
error_mag = error_mag | 2;
end
end
mag_on_start = tics;
end else begin // if ( mag_on )
if( monitor_mag ) $display("Mag off.");
mag_on_total = mag_on_total + tics - mag_on_start;
end // else: !if( mag_on )
/// Verify correct magnetron-on time.
//
task verify_cooking;
begin:A
integer correct_mag_tics;
integer delta;
correct_mag_tics = shadow_tics * shadow_power / 10;
delta = abs(correct_mag_tics - mag_on_total);
if( mag_on ) begin
$display("Mag should be off.");
error_mag = error_mag | 'h10;
end
if( delta > 128 ) begin
$display("Wrong power level. %d %d ",
correct_mag_tics, mag_on_total);
error_mag = error_mag | 4;
end
end // block: A
endtask // verify_cooking
/// Watch display, etc.
//
always @( dmt or dmu or dst or dsu or shadow_display ) #1 begin
if( monitor_display )
$display("Display: %h sh: %h alt: %h secs %d, state %d",
mod_digits, shadow_display,alt_display,
shadow_secs, shadow_state);
if( shadow_state == ss_cook ) begin
shadow_secs = shadow_secs - 1;
shadow_tics = shadow_tics + 64;
tosecs(mod_secs);
if( mod_secs !== shadow_secs )
begin
$display("Wrong count. (cooking)");
error_display = error_display | 4;
end
delta = shadow_tics - ( tics - start_tics );
if( abs(delta) > 96 )
begin
$display("More than 96 tics off: %d",delta);
error_display = error_display | 2;
end
if( mod_digits == 0 ) begin
expecting_done_beep = 1;
delay(1.5);
verify_cooking;
if( expecting_done_beep ) begin
$display("End of cooking beep missing.");
error_beep = error_beep | 4;
end
shadow_state = ss_reset;
shadow_display = 0;
end else if ( shadow_secs == 0 )
begin
$display("Count problem.");
end
end else if ( shadow_state == ss_pause ) begin
tosecs(mod_secs);
if( mod_secs !== shadow_secs ) begin
$display("Wrong count. (paused)");
error_display = error_display | 8;
end
end else if ( watch_display ) begin
if( mod_digits !== shadow_display
&& ( alt_disp_stale <= tics || mod_digits !== alt_display ) )
begin
$display("Wrong display, should be %h or maybe %h but not %h",
shadow_display,alt_display,mod_digits);
error_display = error_display | 'h10;
end
end
end
// Reset shadow state and expected outputs maintained by
// testbench.
//
task to_reset;
begin
shadow_digits = 0;
alt_display = shadow_display;
alt_disp_stale = tics + 2;
shadow_state = ss_reset;
shadow_display = 0;
shadow_power = 10;
watch_display = 1;
end
endtask // to_reset
// Send keys to module, update correct state and expected output information.
//
task command;
input [799:0] cmd;
integer initialized;
integer c;
integer consec_reset;
reg [5:0] to_key [0:255];
reg [5:0] key;
begin
if( initialized === 'bx ) begin
for( c = 0; c < 256; c = c + 1 ) to_key[c] = key_never;
for( c = 0; c < 10; c = c + 1 ) to_key[ "0" + c ] = key_0 + c;
to_key["s"] = key_start;
to_key["r"] = key_reset;
to_key["p"] = key_power;
to_key[" "] = key_none;
to_key[0] = key_none;
initialized = 1;
consec_reset = 0;
end // if ( initialized === 'bx )
while( cmd ) begin:COMMAND_LOOP
reg [7:0] c;
reg [5:0] key_type;
c = cmd[799:792];
cmd = cmd << 8;
if( c == 0 || c == " " ) disable COMMAND_LOOP;
key_mod = to_key[ c ];
key = key_mod;
key_type = ( c >= "0" && c <= "9" ) ? kty_digit : key_mod;
if( key == key_never ) begin
$display("Testbench error: illegal key in command, %s (%d)",c,c);
$stop;
end
if( key != key_none )
consec_reset = key == key_reset ? consec_reset + 1 : 0;
if( monitor_keys && key != key_none && consec_reset < 3 )
$display("Key %s ", c);
casez( {shadow_state,key_type} )
{3'b???,key_none}:;
{ss_reset,key_reset}:
begin
to_reset;
end
{ss_reset,kty_digit}:
begin
shadow_digits = 1;
shadow_state = ss_digit1;
alt_display = shadow_display;
alt_disp_stale = tics + 2;
shadow_display = key-key_0;
end
{ss_pause,key_reset},
{ss_digit1,key_reset},
{ss_digit2,key_reset}:
begin
to_reset;
end
{ss_digit1,kty_digit}:
begin
shadow_digits = 2;
shadow_state = ss_digit2;
alt_display = shadow_display;
alt_disp_stale = tics + 2;
shadow_display = (shadow_display << 4) | key-key_0;
end
{ss_digit1,key_power}:
begin
shadow_state = ss_digit2;
shadow_digits = 0;
alt_display = shadow_display; // Power level.
alt_disp_stale = 'h7fffffff;
shadow_power = shadow_display;
shadow_display = 0;
end
{ss_digit1,key_start},{ss_digit2,key_start}:
begin
tosecs(shadow_secs);
start_tics = tics;
shadow_tics = 0;
mag_on_total = 0;
mag_on_start = 0;
shadow_state = ss_cook;
end
{ss_digit2,kty_digit}:
if( shadow_digits == 4 )
expected_beep_done = tics + 16;
else
begin
shadow_digits = shadow_digits + 1;
// If shadow_display zero then power was pressed.
alt_disp_stale = tics + 2;
alt_display = mod_digits;
shadow_display = (shadow_display << 4) | key-key_0;
end
{ss_cook,key_reset}:
begin
shadow_state = ss_pause;
shadow_display = mod_digits;
pause_tics = tics;
end
{ss_pause,key_start}:
begin
verify_cooking;
mag_on_start = 0;
shadow_tics = 0;
mag_on_total = 0;
start_tics = tics;
shadow_state = ss_cook;
end
default:
begin
if( expected_beep_done && expected_beep_done < tics )
begin
$display("Missed a beep. (overlap)");
error_beep = error_beep | 8;
end
expected_beep_done = tics + 16;
end
endcase // casez( {shadow_state,key_type} )
@( posedge clk ) @( negedge clk );
repeat ( $random() & 15 + 3 ) @( negedge clk );
key_mod = key_none;
@( posedge clk ) @( negedge clk );
@( posedge clk ) @( negedge clk );
end // block: COMMAND_LOOP
end
endtask // command
// Reset oven module either using reset line or
// reset button.
//
task reset_oven;
input hard;
begin
if( hard ) begin
reset = 1;
fork:F
begin repeat ( 5 * 60 * 64 ) @( posedge clk ); disable F; end
wait( !beep );
wait( !mag_on );
join
reset = 0;
end else begin
command("rrrrrr");
fork:T
wait( !beep );
wait( !mag_on );
begin repeat ( 5 * 60 * 64 ) @( posedge clk ); disable T; end
join
command("rrrrrr");
end // else: !if( 0 && hard )
if( beep || mag_on )
begin
$display("Could not reset oven.");
if( beep ) error_beep = error_beep | 'h20;
if( mag_on ) error_mag = error_mag | 8;
end
end
endtask // reset_oven
// Actions to be done at the end of a test.
//
task endtest;
input [159:0] name;
integer error_count;
begin
if( expected_beep_done )
begin
if( expected_beep_done >= tics )
$display("Testbench not waiting long enough for beep.");
$display("Missed a beep.");
error_beep = error_beep | 'h10;
expected_beep_done = 0;
end
if( mod_digits!==0 )
begin
$display("Display should be zero.");
error_display = error_display + 'h20;
end
verify_cooking;
watch_display = 0;
error_count = ( error_display ? 1 : 0 )
+ ( error_beep ? 1 : 0 ) + ( error_mag ? 1 : 0 );
error_total = error_total + error_count;
error_beep_total = error_beep_total | error_beep;
error_mag_total = error_mag_total | error_mag;
error_display_total = error_display_total | error_display;
$display("OUTCOME: %s on test %s. (dsp,beep,mag) (%2h,%2h,%2h)",
error_count ? "FAIL" : "PASS",
name,
error_display, error_beep, error_mag);
reset_oven(0);
error_display = 0;
error_beep = 0;
error_mag = 0;
mag_on_total = 0;
shadow_tics = 0;
end
endtask // endtest
task delay;
input [63:0] secs;
#( secs * 1000000 );
endtask // delay
initial begin
monitor_display = 1;
monitor_keys = 1;
monitor_beep = 1;
monitor_mag = 1;
patient = 1;
expected_beep_done = 0;
expecting_done_beep = 0;
error_display = 0;
error_display_total = 0;
error_beep = 0;
error_beep_total = 0;
error_mag = 0;
error_mag_total = 0;
error_total = 0;
mag_on_total = 0;
key_mod = key_never;
#1;
to_reset;
reset_oven(1);
error_display = 0;
error_display_total = 0;
error_beep = 0;
error_beep_total = 0;
error_mag = 0;
error_mag_total = 0;
error_total = 0;
mag_on_total = 0;
command("50prr");
delay(54);
endtest("Power Too High");
command("32s"); delay(36);
endtest("Basic");
if( patient ) begin:PATIENT
integer old_mon, old_mag;
old_mon = monitor_display;
old_mag = monitor_mag;
monitor_display = 0;
$display("Starting test Long, be patient or modify testbench.");
command("1234s"); delay(14*60);
endtest("Long");
monitor_display = old_mon;
monitor_mag = old_mag;
end
command("30s"); delay(14);
command("1"); delay(2); command("p");
delay(20);
endtest("Basic Disturbed");
command("5p30s");
delay(40);
endtest("Half Power");
command("9p3p0s");
delay(40);
endtest("Power Twice");
command("3ppp19s");
delay(40);
endtest("Power Thrice");
command("20s");
delay(10);
command("r");
delay(5);
command("s");
delay(16);
endtest("Reset Start");
command("20s");
delay(10);
command("r");
delay(5);
command("r");
delay(16);
endtest("Reset Reset");
command("s");
delay(5);
endtest("Null Start");
command("ps");
delay(5);
endtest("Null Power Start");
command("7p30s"); delay(10);
command("1"); delay(2); command("s"); delay(3);
command("12344321");
delay(40);
endtest("Power Disturbed");
command("12r5s"); delay(135);
endtest("Twelve no 5");
command("12345rr"); delay(5);
endtest("Display Overflow");
if( show_key ) begin
$display("\n ** Error Codes **
Display (Codes in hexadecimal. Error codes or'ed together.)
1 Digit out of range. (Check digit.)
2 Display digit change more than 96 tics off.
4 Wrong count. (cooking)
8 Wrong count. (paused)
10 Wrong count displayed. (Not cooking nor paused.)
20 Display should be zero.
Beep:
1 Should not be beeping.
2 Beep duration wrong.
4 End of cooking beep missing.
8 Missed a beep. (overlap)
10 Missed a beep. (endtest)
20 Wouldn't stop beeping!
Mag
1 Mag on when cooking off.
2 Mag cycle error.
4 Wrong power level.
8 Would not turn off.
10 Should be off (when cooking verified).
");
$display("\n ** Testbench Monitoring ** ");
$display(" Display %s", monitor_display ? "on" : "off");
$display(" Keys %s", monitor_keys ? "on" : "off");
$display(" Beep %s", monitor_beep ? "on" : "off");
$display(" Mag %s", monitor_mag ? "on" : "off");
$display(" To turn monitoring on and off edit monitor_FOO variables.");
$display(" at last \"initial begin\" in testbench.\n\n");
end
$display("%s, beep tests (code: %h).",
error_beep_total ? "FAIL" : "PASS", error_beep_total);
$display("%s, mag tests (code: %h).",
error_mag_total ? "FAIL" : "PASS", error_mag_total);
$display("%s, display tests (code: %h).",
error_display_total ? "FAIL" : "PASS", error_display_total);
$display("All tests completed, %d total errors.",error_total);
$stop;
end // initial begin
endmodule // test_oven
// exemplar translate_on