Friday, March 18, 2011

Remembering classic logic... (part 6)

Recently I pulled out my boxes of 7400 series logic (working on a few old school projects that were idled for a bit).  There's something special about these chips...

So, on a whim I thought that I'd bang out verilog modules for the chips I have on hand.  I'm putting the files for these modules in the tanukifu project here.

We've seen how a latch or a register (flip-flop) can be used to store the state of a wire (high or low) after the signal on that wire has changed... but what if you wanted to remember more than one state?  If you chain a set of flip-flops together you can make a shift register where each time a new state is sampled the previously sampled state is passed off into the neighboring register in the chain.

The 74x164 is a simple collection of eight registers chained together where the two inputs (Ain1 and Ain2) determine the next state of the output A1 when the clock goes from low to high (positive edge).  At the same time the pervious state of A1 is passed to A2 and so on (the previous state of A8 is forgotten).  An async. clear line allows all the outputs (A1->A8) to be reset to a low state.

The hdl is simple for this... (I am explicitly stating each register here rather than grouping them together in a set because it's easier to see exactly what happens).


   always @ (posedge clock or negedge clear) begin    
      if (!clear) begin
         A1 <= 1'b0;
         A2 <= 1'b0;
         A3 <= 1'b0;
         A4 <= 1'b0;
         A5 <= 1'b0;
         A6 <= 1'b0;
         A7 <= 1'b0;
         A8 <= 1'b0;    
      end
      else if (clear) begin  
         A8 <= A7;      
         A7 <= A6;      
         A6 <= A5;      
         A5 <= A4;      
         A4 <= A3;      
         A3 <= A2;      
         A2 <= A1;      
         A1 <= (Ain1 & Ain2);   
      end
   end
A similar chip (the 74x194) reduces the count from 8 states to 4 states but adds the ability to shift data in from either the left or right side of the set.  It also adds the concept of parallel loading (all the outputs (A1->A4) can be set at once from the parallel inputs (P0->P4).  This makes an extremely useful building block for creating more complicated shift registers and also open the possibility of grabbing data from a parallel bus and then shifting it out to a serial bus.  The hdl is a little more complicated in verilog but not bad...

 // Shift register has it's own clock and async. clear
   // mode (par load, shft left, shft right) set by s0 and s1  
   always @ (posedge clock or negedge reset) begin    
      if (!reset) begin
         A1 <= 1'b0;
         A2 <= 1'b0;
         A3 <= 1'b0;
         A4 <= 1'b0; 
      end
      else if (reset & s0 & s1) begin
         // parallel load mode
         A1 <= P1;
         A2 <= P2;
         A3 <= P3;
         A4 <= P4;      
      end
      else if (reset & !s0 & s1) begin
         // shift right mode
         A4 <= A3;
         A3 <= A2;
         A2 <= A1;
         A1 <= inright; 
      end
      else if (reset & s0 & !s1) begin
         // shift left mode
         A1 <= A2;
         A2 <= A3;
         A3 <= A4;
         A4 <= inleft;  
      end
   end // always @ (posedge serclk or negedge clear)
The 74x595 is one of my favorite shift register variants because it includes a second set of internal registers connected to the outputs.  By using a different clocks for setting the outputs and for shift in data we can make the parallel outputs all change at the same time and it allows us to tristate the outputs (so connecting to a bus is very easy).  By including the Aout line it's easy to chain multiple chips together to act as a larger shift register.  The verilog hdl is a bit more complex but straightforward if you think of it as two linked chips (a shift register and a tristate 8 bit set of registers).  This is still one of my most used chips because it's just so practical...

 // This allways chaining of the shift register
   assign Aout = A8shift;

   // tristate the output registers
   assign A1 = !enable ? A1out : 1'bz;
   assign A2 = !enable ? A2out : 1'bz;
   assign A3 = !enable ? A3out : 1'bz;
   assign A4 = !enable ? A4out : 1'bz;
   assign A5 = !enable ? A5out : 1'bz;
   assign A6 = !enable ? A6out : 1'bz;
   assign A7 = !enable ? A7out : 1'bz;
   assign A8 = !enable ? A8out : 1'bz;
  
   // Shift register has it's own clock and async. slear
   always @ (posedge serclk or negedge clear) begin    
      if (!clear) begin
         A1shift <= 1'b0;
         A2shift <= 1'b0;
         A3shift <= 1'b0;
         A4shift <= 1'b0;
         A5shift <= 1'b0;
         A6shift <= 1'b0;
         A7shift <= 1'b0;
         A8shift <= 1'b0;    
      end
      else if (clear) begin  
         A8shift <= A7shift;      
         A7shift <= A6shift;      
         A6shift <= A5shift;      
         A5shift <= A4shift;      
         A4shift <= A3shift;      
         A3shift <= A2shift;      
         A2shift <= A1shift;      
         A1shift <= Ain;   
      end
   end // always @ (posedge serclk or negedge clear)

   // separate clock to transfer the shift register in bulk to the output registers
   always @ (posedge regclk) begin
      A1out <= A1shift;
      A2out <= A2shift;
      A3out <= A3shift;
      A4out <= A4shift;
      A5out <= A5shift;
      A6out <= A6shift;
      A7out <= A7shift;
      A8out <= A8shift;     
   end
 The last shift register in my box is the 74x673... Here we have 16 bits as a parallel interface that can be used to present the internal shift register state to a bus, or it can be used to set the internal shift registers from the bus.  There is a single serial line that can be used to put data in or to shift data out (and here there is recirculation of the bits from the end of the chain so after 16 shifts the registers look exactly like they did at the start and no data is lost).  This becomes a bit more complicated in verilog, but it's still not too difficult to understand... Because of the large number of interconnections needed to give this much flexibility, the mapping becomes more complex as well...

  // tristate the output registers
   assign A1 = reset ? A1out : 1'bz;
   assign A2 = reset ? A2out : 1'bz;
   assign A3 = reset ? A3out : 1'bz;
   assign A4 = reset ? A4out : 1'bz;
   assign A5 = reset ? A5out : 1'bz;
   assign A6 = reset ? A6out : 1'bz;
   assign A7 = reset ? A7out : 1'bz;
   assign A8 = reset ? A8out : 1'bz;
   assign A9 = reset ? A9out : 1'bz;
   assign A10 = reset ? A10out : 1'bz;
   assign A11 = reset ? A11out : 1'bz;
   assign A12 = reset ? A12out : 1'bz;
   assign A13 = reset ? A13out : 1'bz;
   assign A14 = reset ? A14out : 1'bz;
   assign A15 = reset ? A15out : 1'bz;
   assign A16 = reset ? A16out : 1'bz;
   assign SIO = (!CS & RW) ? A16shift : 1'bz;
     
   // Shift register has it's own clock and async. reset
   always @ (negedge SHCP) begin    
     if (!CS & !RW) begin
     // serial shift load
         A16shift <= A15shift; 
         A15shift <= A14shift; 
         A14shift <= A13shift; 
         A13shift <= A12shift; 
         A12shift <= A11shift; 
         A11shift <= A10shift; 
         A10shift <= A9shift; 
         A9shift <= A8shift; 
         A8shift <= A7shift;      
         A7shift <= A6shift;      
         A6shift <= A5shift;      
         A5shift <= A4shift;      
         A4shift <= A3shift;      
         A3shift <= A2shift;      
         A2shift <= A1shift;      
         A1shift <= SIO;   
      end
      else if (!CS & RW & STCP) begin
     // parallel load no shift     
         A1shift <= A1;
         A2shift <= A2;
         A3shift <= A3;
         A4shift <= A4;
         A5shift <= A5;
         A6shift <= A6;
         A7shift <= A7;
         A8shift <= A8;    
         A9shift <= A9;    
         A10shift <= A10;    
         A11shift <= A11;    
         A12shift <= A12;    
         A13shift <= A13;    
         A14shift <= A14;    
         A15shift <= A15;    
         A16shift <= A16;    
     end
      else if (!CS & RW & !STCP) begin
     // serial output with recirculation
     A16shift <= A15shift; 
         A15shift <= A14shift; 
         A14shift <= A13shift; 
         A13shift <= A12shift; 
         A12shift <= A11shift; 
         A11shift <= A10shift; 
         A10shift <= A9shift; 
         A9shift <= A8shift; 
         A8shift <= A7shift;      
         A7shift <= A6shift;      
         A6shift <= A5shift;      
         A5shift <= A4shift;      
         A4shift <= A3shift;      
         A3shift <= A2shift;      
         A2shift <= A1shift;      
         A1shift <= A16shift;   
     end
   end // always @ (posedge serclk or negedge clear)

   // separate clock to transfer the shift register in bulk to the output registers
   always @ (posedge STCP or negedge reset) begin
      if (!reset) begin
         A1out <= 1'b0;
         A2out <= 1'b0;
         A3out <= 1'b0;
         A4out <= 1'b0;
         A5out <= 1'b0;
         A6out <= 1'b0;
         A7out <= 1'b0;
         A8out <= 1'b0;    
         A9out <= 1'b0;    
         A10out <= 1'b0;    
         A11out <= 1'b0;    
         A12out <= 1'b0;    
         A13out <= 1'b0;    
         A14out <= 1'b0;    
         A15out <= 1'b0;    
         A16out <= 1'b0;    
    end
      else if (reset & !CS & !RW) begin
     A1out <= A1shift;
     A2out <= A2shift;
     A3out <= A3shift;
     A4out <= A4shift;
     A5out <= A5shift;
     A6out <= A6shift;
     A7out <= A7shift;
     A8out <= A8shift; 
     A9out <= A9shift; 
     A10out <= A10shift; 
     A11out <= A11shift; 
     A12out <= A12shift; 
     A13out <= A13shift; 
     A14out <= A14shift; 
     A15out <= A15shift; 
     A16out <= A16shift;
      end
   end
These are still very useful chips to have around - more and more it seems that designs are becoming so dense and timing requirements are becoming so tight that connecting things using a parallel bus isn't practical as often as it used to be... changing data between the two forms is more an more important to understand.  The neat thing about programmable logic is that in your design you can optimize the way you want this to occur (but it's important to understand the reasons that some approaches were so popular that people could make a profit mass producing them).

Next time I think we'll do muxes (very easy)...