/// Code for LSU EE 4702 Spring 2001

/// Calculator Example

/// Changes for Synthesis


`define crange 7:0

module calc(display_val,beep,key_code,reset,clk);
   input [5:0] key_code;
   input       reset, clk;
   output      display_val, 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 [31:0] 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;
           key_divide : do_op = acc - buffer;
           default    :
             begin
                // exemplar translate_off
                $display("Error in behavioral description.");
                $stop;
                // exemplar translate_on
                do_op = 0;  // Pacify compiler.
             end
         endcase // case( pending_op )
      end
   endfunction // do_op

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

   reg [5:0] beep_time;
   reg       beep_ack, beep_req;
   reg       beep;
   //  assign beep = | beep_time;
   //  always #1 if( beep_time ) beep_time = beep_time - 1;
   always @( negedge clk )
     if( reset ) begin
        beep_ack = 0;
        beep_time = 0;
     end else if( beep_ack ^ beep_req ) begin
        beep_ack = !beep_ack;
        beep_time = 20;
        beep = 1;
     end else 
       if( beep_time ) beep_time = beep_time - 1; else beep = 0;

   reg    nl;
   //   always @( key_code ) if( key_code != key_none ) begin
   always @( posedge clk )
     if( reset ) begin
        beep_req = 0;
        nl = 0;
        state = st_0N;
        buffer = 0;
        acc = 0;
        pending_op = 0;
     end else if( key_code == key_none )
       nl = 1;
     else if( nl ) begin
        nl = 0;

        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_req = !beep_req; next_state = state; end

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

        endcase

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

endmodule // calc


// exemplar translate_off

module demo_calc();

   wire [`crange] display;
   reg [5:0]   key;
   reg         clk, reset;
   
   calc c1(display,beep,key,reset,clk);
`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];

            @( posedge clk ) @( negedge clk )
            key = to_key[ c ];
            
            if( key == key_never ) begin
               $display("Demo error: illegal key in command, %s (%d)",c,c);
               $stop;
            end

            //  #1;
            @( posedge clk ) @( negedge clk )

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

            @( posedge clk ) @( negedge clk )

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

   always #1 clk = ~clk;

   initial begin
      clk = 0;
      reset = 0; #2; reset = 1; #2; reset = 0; #2;

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

      $stop;

   end
   

endmodule // demo_calc

// exemplar translate_on