// Associative memory implemented with a linear search.

// For use in LSU EE 4702 Spring 2000.

// Intended to show how an implicit state machine description can be
// transformed into an explicit state machine description.  A
// practical associative memory would not use a sequential linear
// search.

// All modules pass the testbench (amt.v).  All but the first module
// are synthesizable by Leonardo 1999.1f and the synthesized Verilog
// passes the testbench. (The synthesized implicit state machine will
// only work if the flip-flop primitives are redefined so they
// initialize to state zero.  This is due to a shortcoming of Leonardo
// or the Leonardo documentation.)

// The costs and clock frequencies of the modules appear at the
// end of this file.

// Testbench is in file amt.v

// Uncomment one of the AM_foo macros below.

// Non-synthesizable version.
//`define AM_NOT_SYN
// Implicit state machine.
//`define AM_SYN_ISM
// Explicit state machine based on explicit state machine.
//`define AM_SYN_ESM_1
// Streamlined explicit state machine
//`define AM_SYN_ESM_2

`define AM_SYN_ISM_SH


`define dsize 8
`define dbits `dsize-1:0
`define ksize 8
`define kbits `ksize-1:0
`define size_lg 5
`define sbits (`size_lg-1):0
`define size (1<<`size_lg)
`define srange `size-1:0


`ifdef AM_NOT_SYN
module am(data_out,found,ready,full,data_in,key,op,clk);
   input data_in, key, op, clk;
   output data_out, found, ready, full;

   parameter op_reset = 0;
   parameter op_insert = 1;
   parameter op_remove = 2;
   parameter op_lookup = 3;
   parameter op_nop = 4;

   wire [`dbits] data_in;
   wire [`kbits] key;
   wire [2:0]    op;
   wire          clk;
   reg [`dbits]  data_out;
   reg           found, ready;
   wire          full;
   reg [`size_lg:0] occ;

   reg [`dbits]  data [`srange];
   reg [`kbits]  keys [`srange];
   reg           valid [`srange];

   reg [`sbits]  ptr;

   reg           ptr_empty_valid;
   reg [`sbits]  ptr_empty;

   assign        full = occ[`size_lg];
   
   always @( posedge clk ) 
     case( op )

       op_reset:
         begin
            ptr = 0; occ = 0;
            ready = 0; found = 0; 
            begin:RESET_SEARCH
              forever begin
                 valid[ptr] = 0;
                 ptr = ptr + 1;
                 if( !ptr ) disable RESET_SEARCH;
              end
            end
            @( negedge clk ) ready = 1;
         end

       op_insert:
         begin
            ptr = 0; ready = 0; ptr_empty_valid = 0;
            begin:INSERT_SEARCH
              forever begin
                 if( !valid[ptr] && !ptr_empty_valid ) begin
                    ptr_empty_valid = 1;
                    ptr_empty = ptr;
                 end
                 if( valid[ptr] && keys[ptr] === key ) begin
                    found = 1; 
                    data[ptr] = data_in;
                    data_out = data_in;
                    disable INSERT_SEARCH;
                 end
                 ptr = ptr + 1;
                 if( !ptr ) begin  // Loop Exit
                    found = 0;
                    if( ptr_empty_valid ) begin
                       keys[ptr_empty] = key;
                       data[ptr_empty] = data_in;
                       valid[ptr_empty] = 1;
                       occ = occ + 1;
                    end
                    disable INSERT_SEARCH;
                 end
              end // forever begin
            end // block: INSERT_SEARCH
            @( negedge clk ) ready = 1;
         end // case: op_insert

       op_remove, op_lookup:
         begin
            ptr = 0; ready = 0;
            begin:REMOVE_SEARCH
              forever begin
                 if( valid[ptr] && keys[ptr] == key ) begin
                    if( op == op_remove ) begin
                       valid[ptr] = 0;
                       occ = occ - 1;
                    end
                    data_out = data[ptr]; found = 1;
                    disable REMOVE_SEARCH;
                 end
                 ptr = ptr + 1;
                 if( !ptr ) begin found = 0; disable REMOVE_SEARCH; end
              end
            end
            @( negedge clk ) ready = 1;
         end // case: op_remove

       // Do nothing.
       op_nop: @( negedge clk ) ready = 1;

       default: begin
          $display("Invalid operation: %d",op);
          $stop;
       end

     endcase // case( op )
   
endmodule // am
`endif // ifdef AM_NOT_SYN

//
// Using an implicit state machine.
//

`ifdef AM_SYN_ISM

// Note: For simulation of synthesized module, zero must be a valid
// state of the implicit state variable and so binary or gray coding
// must be used.  This is a bug workaround.
//
// exemplar encoding binary
//
module am(data_out,found,ready,full,data_in,key,op,clk);
   input data_in, key, op, clk;
   output data_out, found, ready, full;

   parameter op_reset = 0;
   parameter op_insert = 1;
   parameter op_remove = 2;
   parameter op_lookup = 3;
   parameter op_nop = 4;

   wire [`dbits] data_in;
   wire [`kbits] key;
   wire [2:0]    op;
   wire          clk;
   reg [`dbits]  data_out;
   reg           found, ready;
   wire          full;
   reg [`size_lg:0] occ;

   reg [`dbits]  data [`srange];
   reg [`kbits]  keys [`srange];
   reg           valid [`srange];

   reg [`sbits]  ptr;

   reg           ptr_empty_valid;
   reg [`sbits]  ptr_empty;

   assign        full = occ[`size_lg];

   always @( posedge clk ) begin
      // State 0: ready
     case( op )

       op_reset:
         begin
            ptr = 0; occ = 0;
            ready = 0; found = 0; 
            begin:RESET_SEARCH
              forever @( posedge clk ) begin
                 // State 1: reset_item
                 valid[ptr] = 0;
                 ptr = ptr + 1;
                 if( !ptr ) disable RESET_SEARCH;
              end
            end
            ready = 1;
         end

       op_insert:
         begin
            ptr = 0; ready = 0; ptr_empty_valid = 0;
            begin:INSERT_SEARCH
              forever @( posedge clk ) begin
                 // State 2: insert
                 if( !ptr_empty_valid && !valid[ptr] ) begin
                    ptr_empty_valid = 1;
                    ptr_empty = ptr;
                 end
                 if( valid[ptr] && keys[ptr] === key ) begin
                    found = 1; 
                    data[ptr] = data_in;
                    data_out = data_in; 
                    disable INSERT_SEARCH;
                 end
                 ptr = ptr + 1;
                 if( !ptr ) begin
                    found = 0;
                    if( ptr_empty_valid ) begin
                       keys[ptr_empty] = key;
                       data[ptr_empty] = data_in;
                       valid[ptr_empty] = 1;
                       occ = occ + 1;
                    end
                    disable INSERT_SEARCH;
                 end
              end // forever begin
            end // block: INSERT_SEARCH
            
            ready = 1;
         end // case: op_insert

       op_remove, op_lookup:
         begin
            ptr = 0; ready = 0;
            begin:REMOVE_SEARCH
              forever @( posedge clk ) begin
                 // State 3: rem_look
                 if( valid[ptr] && keys[ptr] === key ) begin
                    if( op == op_remove ) begin
                       valid[ptr] = 0;
                       occ = occ - 1;
                    end
                    data_out = data[ptr]; found = 1;
                    disable REMOVE_SEARCH;
                 end
                 ptr = ptr + 1;
                 if( !ptr ) begin
                    found = 0;
                    disable REMOVE_SEARCH;
                 end
              end
            end
            ready = 1;
         end // case: op_remove

       // Do nothing.
       op_nop: @( posedge clk ) ready = 1;

       default: begin
          // exemplar translate_off
          $display("Invalid operation: %d",op);
          $stop;
          // exemplar translate_on
       end

     endcase // case( op )
   end // block: MAIN

endmodule // am
`endif 

`ifdef AM_SYN_ISM_SH

// Note: For simulation of synthesized module, zero must be a valid
// state of the implicit state variable and so binary or gray coding
// must be used.  This is a bug workaround.
//
// exemplar encoding binary
//
module am(data_out,found,ready,full,data_in,key,op,clk);
   input data_in, key, op, clk;
   output data_out, found, ready, full;

   parameter op_reset = 0;
   parameter op_insert = 1;
   parameter op_remove = 2;
   parameter op_lookup = 3;
   parameter op_nop = 4;

   wire [`dbits] data_in;
   wire [`kbits] key;
   wire [2:0]    op;
   wire          clk;
   reg [`dbits]  data_out;
   reg           found, ready;
   wire          full;
   reg [`size_lg:0] occ;

   reg [`dbits]     data [`srange], temp_data;
   reg [`kbits]     keys [`srange], temp_keys;
   reg              valid [`srange], temp_valid;

   reg [`sbits]  ptr;

   reg           ptr_empty_valid;
   reg [`sbits]  ptr_empty;

   assign        full = occ[`size_lg];
   integer       i;
   
   task shift;
      begin
         temp_data = data[0]; temp_keys = keys[0]; temp_valid = valid[0];
         for(i=0; i<`size-1; i=i+1) begin
            data[i] = data[i+1]; keys[i] = keys[i+1]; valid[i] = valid[i+1];
         end
         data[`size-1] = temp_data;
         keys[`size-1] = temp_keys;
         valid[`size-1] = temp_valid;
      end
   endtask // shift
   
   always @( posedge clk ) begin
      // State 0: ready
     case( op )

       op_reset:
         begin
            ptr = 0; occ = 0;
            ready = 0; found = 0; 
            begin:RESET_SEARCH
              forever @( posedge clk ) begin
                 // State 1: reset_item
                 valid[0] = 0;
                 shift;
                 ptr = ptr + 1;
                 if( !ptr ) disable RESET_SEARCH;
              end
            end
            ready = 1;
         end

       op_insert:
         begin
            ptr = 0; ready = 0; ptr_empty_valid = 0;
            begin:INSERT_SEARCH
              begin:REPLACE_SEARCH forever @( posedge clk ) begin
                 // State 2: insert
                 if( valid[0] && keys[0] === key ) begin
                    found = 1; 
                    data[0] = data_in;
                    data_out = data_in; 
                    disable INSERT_SEARCH;
                 end
                 ptr = ptr + 1;
                 if( !ptr ) disable REPLACE_SEARCH;
                 shift;
              end end
               ptr = 0; found = 0;
               forever @( posedge clk ) begin
                 if( !valid[0] ) begin
                    keys[0] = key;
                    data[0] = data_in;
                    valid[0] = 1;
                    occ = occ + 1;
                    disable INSERT_SEARCH;
                 end
                  ptr = ptr + 1;
                  if( !ptr ) disable INSERT_SEARCH;
                  shift;
               end
            end // block: INSERT_SEARCH
            ready = 1;
         end // case: op_insert

       op_remove, op_lookup:
         begin
            ptr = 0; ready = 0;
            begin:REMOVE_SEARCH
              forever @( posedge clk ) begin
                 // State 3: rem_look
                 if( valid[0] && keys[0] === key ) begin
                    if( op == op_remove ) begin
                       valid[0] = 0;
                       occ = occ - 1;
                    end
                    data_out = data[0]; found = 1;
                    disable REMOVE_SEARCH;
                 end
                 ptr = ptr + 1;
                 shift;
                 if( !ptr ) begin
                    found = 0;
                    disable REMOVE_SEARCH;
                 end
              end
            end
            ready = 1;
         end // case: op_remove

       // Do nothing.
       op_nop: @( posedge clk ) ready = 1;

       default: begin
          // exemplar translate_off
          $display("Invalid operation: %d",op);
          $stop;
          // exemplar translate_on
       end

     endcase // case( op )
   end // block: MAIN

endmodule // am
`endif 


//
// Using an explicit state machine, first attempt. 
//
// Similar to code above (to show how code was modified).  The description
// does simulate and synthesize correctly.
//

`ifdef AM_SYN_ESM_1
// exemplar encoding binary
module am(data_out,found,ready,full,data_in,key,op,clk);
   input data_in, key, op, clk;
   output data_out, found, ready, full;

   parameter op_reset = 0;
   parameter op_insert = 1;
   parameter op_remove = 2;
   parameter op_lookup = 3;
   parameter op_nop = 4;

   wire [`dbits] data_in;
   wire [`kbits] key;
   wire [2:0]    op;
   wire          clk;
   reg [`dbits]  data_out;
   reg           found, ready;
   wire          full;
   reg [`size_lg:0] occ;

   reg [`dbits]  data [`srange];
   reg [`kbits]  keys [`srange];
   reg           valid [`srange];

   reg [`sbits]  ptr;

   reg           ptr_empty_valid;
   reg [`sbits]  ptr_empty;

   parameter st_ready = 0,
             st_reset = 1,
             st_insert = 2,
             st_rem_look = 3;

   reg [1:0] state, next_state;

   assign    full = occ[`size_lg];

   // Note: It would be foolish--and Leonardo won't stand in your way
   // or even warn you--to update clk on the positive edge because
   // next_state is also updated on the positive edge.
   always @( negedge clk ) state <= next_state;
   
   always @( posedge clk ) begin
      case( state )

        st_ready:
          // State 0: ready

          case( op )

            op_reset:
              begin
                 ptr = 0; occ = 0;
                 ready = 0; found = 0;
                 next_state = st_reset;
//              begin:RESET_SEARCH
//                forever @( posedge clk ) begin
//                   // State 1: reset
//                   valid[ptr] = 0;
//                   ptr = ptr + 1;
//                   if( !ptr ) disable RESET_SEARCH;
//                end
//              end
//              ready = 1;
              end

            op_insert:
              begin
                 ptr = 0; ready = 0; ptr_empty_valid = 0;
                 next_state = st_insert;

//                   begin:INSERT_SEARCH
//                     forever @( posedge clk ) begin
//                        // State 2: insert
//                        if( !ptr_empty_valid && !valid[ptr] ) begin
//                           ptr_empty_valid = 1;
//                           ptr_empty = ptr;
//                        end
//                        if( valid[ptr] && keys[ptr] === key ) begin
//                           found = 1; 
//                           data[ptr] = data_in;
//                           data_out = data_in; 
//                           disable INSERT_SEARCH;
//                        end
//                        ptr = ptr + 1;
//                        if( !ptr ) begin
//                           found = 0; 
//                           disable INSERT_SEARCH;
//                        end
//                     end // forever begin
//                   end // block: INSERT_SEARCH
//                   if( !found && ptr_empty_valid ) begin
//                      keys[ptr_empty] = key;
//                      data[ptr_empty] = data_in;
//                      valid[ptr_empty] = 1; occ = occ + 1;
//                   end
//                   ready = 1;
                 
              end // case: op_insert

            op_remove, op_lookup:
              begin
                 ptr = 0; ready = 0;
                 next_state = st_rem_look;

//                   begin:REMOVE_SEARCH
//                     forever @( posedge clk ) begin
//                        // State 3: rem_look
//                        if( valid[ptr] && keys[ptr] == key ) begin
//                           if( op == op_remove ) begin
//                              valid[ptr] = 0;
//                              occ = occ - 1;
//                           end
//                           data_out = data[ptr]; found = 1;
//                           disable REMOVE_SEARCH;
//                        end
//                        ptr = ptr + 1;
//                        if( !ptr ) begin
//                           found = 0;
//                           disable REMOVE_SEARCH;
//                        end
//                     end
//                   end
//                   ready = 1;
                 
              end // case: op_remove

            // Do nothing.
            op_nop: ready = 1;

            default: begin
               // exemplar translate_off
               $display("Invalid operation: %d",op);
               $stop;
               // exemplar translate_on
            end

          endcase // case( op )
        
        st_reset: begin
           
           begin:RESET_SEARCH
             // forever @( posedge clk ) begin
             // State 1: reset
             valid[ptr] = 0;
              ptr = ptr + 1;
              if( !ptr ) begin
                 // disable RESET_SEARCH;
                 next_state = st_ready;
                 ready = 1;
              end
           end
        end
        
        st_insert: begin
           begin:INSERT_SEARCH
             //forever @( posedge clk ) begin
             // State 2: insert
             if( !ptr_empty_valid && !valid[ptr] ) begin
                ptr_empty_valid = 1;
                ptr_empty = ptr;
             end
              if( valid[ptr] && keys[ptr] === key ) begin
                 found = 1; 
                 data[ptr] = data_in;
                 data_out = data_in;
                 ready = 1;
                 next_state = st_ready;
                 disable INSERT_SEARCH;
              end
              ptr = ptr + 1;
              if( !ptr ) begin
                 found = 0; 
                 ready = 1;
                 if( ptr_empty_valid ) begin
                    keys[ptr_empty] = key;
                    data[ptr_empty] = data_in;
                    valid[ptr_empty] = 1;
                    occ = occ + 1;
                 end
                 next_state = st_ready;
                 disable INSERT_SEARCH;
              end
              // end // forever begin
           end // block: INSERT_SEARCH
        end // case: st_insert
        
        st_rem_look: begin
           begin:REMOVE_SEARCH
             //             forever @( posedge clk ) begin
             // State 3: rem_look
             if( valid[ptr] && keys[ptr] == key ) begin
                if( op == op_remove ) begin
                   valid[ptr] = 0;
                   occ = occ - 1;
                end
                data_out = data[ptr]; found = 1;
                ready = 1; next_state = st_ready;
                disable REMOVE_SEARCH;
             end
              ptr = ptr + 1;
              if( !ptr ) begin
                 found = 0;
                 ready = 1; next_state = st_ready;
                 disable REMOVE_SEARCH;
              end
              //             end
           end // block: REMOVE_SEARCH
        end // case: st_rem_look

        // exemplar translate_off
        // Still need an explicit reset.
        2'bxx: next_state = st_ready;
        // exemplar translate_on

        default: begin
           // exemplar translate_off
           $display("Invalid state."); $stop;
           // exemplar translate_on
        end

      endcase // case( state )
      
        
   end // always @ ( posedge clk )

endmodule // am
`endif 


//
// Using an explicit state machine, second attempt.
//
// Based on the code above, but reorganized and streamlined.
//

`ifdef AM_SYN_ESM_2
// exemplar encoding binary
module am(data_out,found,ready,full,data_in,key,op,clk);
   input data_in, key, op, clk;
   output data_out, found, ready, full;

   parameter op_reset = 0;
   parameter op_insert = 1;
   parameter op_remove = 2;
   parameter op_lookup = 3;
   parameter op_nop = 4;

   wire [`dbits] data_in;
   wire [`kbits] key;
   wire [2:0]    op;
   wire          clk;
   reg [`dbits]  data_out;
   reg           found;
   wire          ready;
   wire          full;
   reg [`size_lg:0] occ;

   reg [`dbits]  data [`srange];
   reg [`kbits]  keys [`srange];
   reg           valid [`srange];

   reg [`sbits]  ptr;

   reg           ptr_empty_valid;
   reg [`sbits]  ptr_empty;

   parameter /* exemplar enum states */ st_ready = 0,
             st_reset = 1,
             st_insert = 2,
             st_rem_look = 3;

   reg [1:0] /* exemplar enum states */ state, next_state;

   assign        full = occ[`size_lg];
   assign        ready = state == st_ready;

   // Note: It would be foolish--and Leonardo won't stand in your way
   // or even warn you--to update clk on the positive edge because
   // next_state is also updated on the positive edge.
   always @( negedge clk ) state <= next_state;
   
   always @( posedge clk ) begin

      // Note: The directives below are needed despite the fact that
      // state is an enumerated type and all states appear. (Leonardo 1999.1f)
      // exemplar parallel_case
      // exemplar full_case
      case( state )

        st_ready: // State 0: ready

          // exemplar parallel_case
          // exemplar full_case
          case( op )

            op_reset: begin
               ptr = 0; occ = 0; found = 0;
               next_state = st_reset;
            end

            op_insert: begin
               ptr = 0; ptr_empty_valid = 0;
               next_state = st_insert;
            end 

            op_remove, op_lookup: begin
               ptr = 0; 
               next_state = st_rem_look;
            end 

            // Do nothing.
            op_nop:;

            default: begin
               // exemplar translate_off
               $display("Invalid operation: %d",op);
               $stop;
               // exemplar translate_on
            end

          endcase // case( op )
        
        st_reset: begin // State 1: reset
           valid[ptr] = 0;
           ptr = ptr + 1;
           if( !ptr ) next_state = st_ready;
        end
        
        st_insert: begin // State 2: insert
           if( !valid[ptr] && !ptr_empty_valid ) begin
              ptr_empty_valid = 1;
              ptr_empty = ptr;
           end 
           if( valid[ptr] && keys[ptr] === key ) begin
              data[ptr] = data_in;
              data_out = data_in;
              found = 1; 
              next_state = st_ready;
           end else begin
              ptr = ptr + 1;
              if( !ptr ) begin
                 if( ptr_empty_valid ) begin
                    keys[ptr_empty] = key;
                    data[ptr_empty] = data_in;
                    valid[ptr_empty] = 1;
                    occ = occ + 1;
                 end
                 found = 0; 
                 next_state = st_ready;
              end
           end

        end // case: st_insert
        
        st_rem_look: begin
           // State 3: rem_look
           if( valid[ptr] && keys[ptr] == key ) begin
              if( op == op_remove ) begin valid[ptr] = 0; occ = occ - 1; end
              data_out = data[ptr]; found = 1;
              next_state = st_ready;
           end else begin
              ptr = ptr + 1;
              if( !ptr ) begin
                 found = 0;
                 next_state = st_ready;
              end
           end // else: !if( valid[ptr] && keys[ptr] == key )
        end // case: st_rem_look

        // exemplar translate_off
        // Still need an explicit reset.
        2'bxx: next_state = st_ready;
        // exemplar translate_on

        // exemplar translate_off
        default: begin
           $display("Invalid state."); $stop;
        end
        // exemplar translate_on

      endcase // case( state )
      
        
   end // always @ ( posedge clk )

endmodule // am
`endif 



//  Cost and Performance of Linear Search Associative Memory Variations 

//  Run with "quick" optimization and set to optimize delay. Synthesized
//  for the sample ASIC.


//  Implicit State Machine

//   Number of ports :                      31
//   Number of nets :                     1594
//   Number of instances :                1556
//   Number of references to this view :     0

//   Number of gates :                   10806

//  	clk                  : 120.7 MHz


//  Explicit State Machine 1

//   Number of ports :                      31
//   Number of nets :                     1602
//   Number of instances :                1565
//   Number of references to this view :     0

//   Number of gates :                   10800

//  	clk                  : 128.2 MHz



//  Explicit State Machine 2


//   Number of ports :                      31
//   Number of nets :                     1563
//   Number of instances :                1531
//   Number of references to this view :     0

//   Number of gates :                   10710

//  	clk                  : 120.2 MHz