/// Code for LSU EE 4702 Spring 2001

/// Calculator Example

// Calculator Precision
`define crange 7:0

module calc(display_val,beep,key_code);
   input [5:0] key_code;
   output      display_val;
   output      beep;

`include "calc_keys.v"
   
   parameter kty_digit = 6'd30;
   parameter kty_arith = 6'd31;
   parameter kty_any   = 6'bx;

   reg [2:0] state, next_state;
   
   parameter st_0N = 3'd0;
   parameter st_0P = 3'd1;
   parameter st_0C = 3'd2;
   parameter st_1N = 3'd3;
   parameter st_1P = 3'd4;
   parameter st_xx = 3'bxxx;

   parameter buffer_max = 'h7fffffff;
   
   reg [`crange] buffer, acc;
   reg [5:0]  pending_op, key_type;

   wire [`crange] display_val = buffer;

   task add_digit;
      begin
         if( buffer < buffer_max )
           buffer = buffer * 10 + key_code - key_0;
      end
   endtask // add_digit

   function [`crange] do_op;
      input dummy;
      begin
         case( pending_op )
           key_plus   : do_op = acc + buffer;
           key_minus  : do_op = acc - buffer;
           key_times  : do_op = acc * buffer;
           key_divide : do_op = buffer ? acc / buffer : 0;
         endcase // case( pending_op )
      end
   endfunction // do_op

   task fatal;
      input [319:0] message;
      begin
         $display("Error in behavioral description: %s",message);
         $display("Execution stopping.");
         $stop;
      end
   endtask // fatal

   reg [5:0] beep_time;
   assign beep = | beep_time;
   always #1 if( beep_time ) beep_time = beep_time - 1;

   task do_beep;
      $display("Beeeeeep!!!  %d",state);
   endtask // do_beep

   always @( key_code ) if( key_code != key_none ) begin

      case( key_code )
        key_0,key_1,key_2,key_3,key_4,key_5,key_6,key_7,key_8,key_9:
          key_type = kty_digit;
        key_plus,key_minus,key_times,key_divide:
          key_type = kty_arith;
        default:
          key_type = key_code;
      endcase // case( key_code )

      casex( {state,key_type} )

        {st_xx,key_clear}: begin acc = 0;  buffer = 0; next_state = st_0N;  end

        {st_0N,kty_digit}: begin add_digit;  next_state = st_0P; end

        {st_0P,kty_digit}: begin add_digit;  next_state = state; end

        {st_0P,kty_arith}: 
          begin 
             pending_op = key_code;  
             acc        = buffer;
             buffer     = 0;
             next_state = st_1N;
          end

        {st_0C,kty_digit}:
          begin
             buffer = 0;
             add_digit;
             next_state = st_0P;
          end

        {st_0C,kty_arith}:
          begin
             pending_op = key_code;
             acc        = buffer;
             buffer     = 0;
             next_state = st_1N;
          end

        {st_1N,kty_digit}: begin add_digit;  next_state = st_1P; end

        {st_1P,kty_digit}: begin add_digit;  next_state = state; end

        {st_1P,key_equal}:
          begin
             buffer = do_op(0);
             next_state = st_0C;
          end

        {st_1P,kty_arith}:
          begin
             acc = do_op(0);
             pending_op = key_code;
             buffer = 0;
             next_state = st_1N;
          end

        {st_0N,kty_any}, {st_0C,kty_any}, {st_0P,kty_any},
            {st_1N,kty_any}, {st_1P,kty_any}:
              begin
                 beep_time = 10;
                 next_state = state;
              end

        default: 
          begin
             $display("Behavioral code error, unexpected state.");
             $display("Stopping.");
          end

      endcase // casex( {state,key_type} )

      state <= next_state;
      
   end // if ( key_code != key_none )

endmodule // calc

module demo_calc();

   wire [`crange] display;
   reg [5:0]   key;
   wire        beep;
   
   calc c1(display,beep,key);

`include "calc_keys.v"

   task command;

      input [799:0] cmd;

      integer initialized;
      integer c;
      reg [5:0] to_key [0:255];

      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["+"] = key_plus;
            to_key["-"] = key_minus;
            to_key["/"] = key_divide;
            to_key["*"] = key_times;
            to_key["="] = key_equal;
            to_key["c"] = key_clear;
            to_key[" "] = key_none;
            to_key[0]   = key_none;

            initialized = 1;

         end // if ( initialized === 'bx )

         while( cmd ) begin:COMMAND_LOOP
           reg [7:0] c;

            c   = cmd[799:792];
            key = to_key[ c ];
            
            if( key == key_never ) begin
               $display("Demo error: illegal key in command, %s (%d)",c,c);
               $stop;
            end

            #1;

            if( key != key_none ) $display("Key %s  Display %d",c,display);

            key = key_none;

            #1;

            cmd = cmd << 8;

         end // block: COMMAND_LOOP

      end
      
   endtask // command

   always @( posedge beep ) $display("Beep starting.");
   always @( negedge beep ) $display("Beep finished.");

   initial begin

      command("c 5 c 12 + 34 = ");
      
      command(" 1 + 2 + 3 ++ 4 = - 10 = ");

      $stop;

   end
   

endmodule // demo_calc