Monday, February 28, 2011

Finishing up a TWI/I2C interface...

Well so far we've made a few modules (a clock, a tristate controller, a signal transmitter and a signal receiver) so far and with a few more small ones we will have the core of a hardware twi/i2c controller.

One thing we need to so is refine our handling of the alarms from the tristate module (these go high when the intended signal doesn't match the output signal).  the problem is that in practice it takes a non-zero amount of time for levels to change and for us to detect those changes.

A simple way to handle short term differences that could be acceptable is to only send that alarm if they exist for too long. Here we will do that with shift register.  The full verilog for the design is here.

The other thing we need to consider is synchronizing the SCL clock.  This needs to be done when the bus is stretched or a second master has stepped on (transmitted over) our clock.



This is very easy to do by inducing a stretch signal if SCL is stretched low and also resetting the intent module so that it can detect errors again in the future.
if (scl_alarm && (scl == 1'b0)) begin
            // we wanted to drive high and failed for long enough to trigger an
            // alarm... so something else is holding scl low (stretching or other scl generator)
            // We should therefore try to get in our low phase of the signal
            stretch_low <= 1'b1; // get our generator in the low phase of the cycle.
            clear       <= 1'b1; // reset intent monitor          
         end
         if (clear) begin
            clear <= 1'b0; // reset flag
         end

Which comes down to a very simple implementation.   The whole verilog design for the module is here.
The last thing we need to add is a filter for the SDA and SCL lines.  If have perfect signals and great connectors and shielding then it's not needed; however, in practice there can be short glitches in the lines. 


   scl_p <= scl;
         sda_p <= sda;

         if ((scl_p == scl) && (scl_history > 0)) begin
            scl_history <= scl_history - 1;        
         end
         else if ((scl_p == scl) && (scl_history == 0)) begin
            scl_filtered <= scl;           
         end
         else  begin
            scl_history <= 3'b111;         
         end
        
         if ((sda_p == scl) && (sda_history > 0)) begin
            sda_history <= sda_history - 1;        
         end
         else if ((sda_p == scl) && (sda_history == 0)) begin
            sda_filtered <= sda;           
         end
         else  begin
            sda_history <= 3'b111;         
         end    

Here we could use simple shift registers, but instead we will count the number of clocks between changes in state and only let the output change if the input signal has been stable for a sufficient amount of time.  The reason is that using a shift register will make the output immediately sensitive to a change int he input after it is stable and using a counter is an easy way to make it resistant to intermittent fluctuations in all cases.  The design is more complicated than using shift registers.
The whole verilog file for this module is here.




Now that we have all the pieces we can build a base module that combines them together.  This module will detect and transmit signals on the bus and can generate the SCL clock if needed. 

This is rather easy to do by connection the inputs and output from the modules we have to each other and the remaining ones to inputs and outputs of our new module.


assign arbitration_lost = sda_alarm & scl;
     
   tf_component_tristate_pair component_tristate_pair (
                                                       .sda(sda),
                                                       .sda_xmt(sda_xmt),
                                                       .sda_status(sda_status),
                                                       .sda_enable(sda_enable),
                                                       .scl(scl),
                                                       .scl_xmt(clock_variable),
                                                       .scl_status(scl_status),
                                                       .scl_enable(scl_enable),
                                                       .stretch(stretch)
                                                       );
                                                                                                                
   tf_component_scl_variable component_scl_variable (
                                                     .clock(clock),
                                                     .reset(reset | !scl_enable | stretch_low_sync),
                                                     .clock_out(clock_variable),
                                                     .stretch_high(stretch_high_xmt),
                                                     .stretch_low(stretch | stretch_low_sync),
                                                     .delay_16bits(delay_16bits)
                                                     );
     
   tf_component_twi_intent component_twi_intent (
                                                 .clock(clock),
                                                 .reset(reset),
                                                 .clear(clear),
                                                 .scl_status(scl_status),
                                                 .sda_status(sda_status),
                                                 .scl_alarm(scl_sync),
                                                 .sda_alarm(sda_alarm)
                                                 );
  
   tf_component_twi_synchro twi_synchro (
                                           .clock(clock),
                                           .reset(reset),
                                           .scl_alarm(scl_sync),
                                           .clear(clear),
                                           .stretch_low(stretch_low_sync),
                                           .scl(scl_status)
                                           );

   tf_component_twi_rec twi_rec (
                                       .clock(clock),
                                       .reset(reset),
                                       .scl(scl_filtered),
                                       .sda(sda_filtered),
                                       .rec_start(detect_start),
                                       .rec_stop(detect_stop),
                                       .rec_data0(detect_data0),
                                       .rec_data1(detect_data1)
                                       );

   tf_component_twi_xmt twi_xmt (
                                 .clock(clock),
                                 .reset(reset),
                                 .scl(scl_filtered),
                                 .stretch_high(stretch_high_xmt),
                                 .sda_xmt(sda_xmt),
                                 .sda_enable(sda_enable),
                                 .xmt_start(xmt_start),
                                 .xmt_stop(xmt_stop),
                                 .xmt_data0(xmt_data0),
                                 .xmt_data1(xmt_data1),
                                 .xmt_ack(xmt_ack),
                                 .pulse_delay(delay_16bits)
                                 );
                                                                                       
   tf_component_twi_filter twi_filter (
                                       .clock(clock),
                                       .reset(reset),
                                       .sda(sda),
                                       .scl(scl),
                                       .sda_filtered(sda_filtered),
                                       .scl_filtered(scl_filtered)
                                       );

The only new assignment here is to bind the arbitration_lost signal (high when SDA state doesn't match intent - something is talking/transmitting over our data) output to be only true when SCL is high.  In theory this should not be necessary, but in practice sometimes slave devices will ack back to the master earlier than they should (rather than waiting for the signal hold time as they are supposed to).
The whole verilog file for the module is here.  This could easily be controlled with software, but that's sometimes too slow... next we will add modules to handle the logical device design levels of addressing, sending/receiving bytes and master and slave behaviors.

Friday, February 25, 2011

Sending twi signals...

If one wants to transmit signals on an I2C/TWI bus more things need to be considered than when one is simply detecting signals sent by another device.  The reason is that while there are only 4 possible timing conditions between SCL and SDA, it matters when these signals occur relative to each other.  In the detector we only have to pay attention to the sequential order, but in a transmitter we need to pay attention to the relationships in real time... Since physical changes (voltages, saturation, gate speed, wire speed) all take non-zero amounts of real time to occur they need to be considered when deciding the exact moment to transmit changes.

This is why we have timing diagrams, hold times, setup times that all components should follow for reliable communication.  For our case, the important thing is that since SCL is used to determine the meaning of SDA changes, we need to make certain that we control the changes in SDA with safe margins relative to SCL.  If we are controlling SCL (we are a master) then it's easy to do; however, if we are acting as a slave then we have to guess when SCL will change to decide when to change SCL.

Sometimes we can force SCL no to change when we aren't ready to send data by stretching SCL low.  If the master supplying the SCL signal releases the line (so it should float high) and a slave pulls SCL low then the master will wait until the line is released by the slave.  There is no way for the slave (or any device) to hold SCL high though...  The way around this is to count the amount of elapsed time from a change in SCL and only change SDA within an interval that isn't too early or too late to meet the timing specifications.  In this design we will simply take the clock divider for generating SCL and divide by two and four to mark an interval that easily meets the timing specs.  The exceptions to this interval occur when the bus is stretched - once a complete SCL cycle has passed and it's still in the same state (being stretched) we allow changes to occur as well. For example...


   // We are going to pay attention to the period between 1/4 and 1/2 of the delay and also when stretched...
   assign half_pulse_delay    = pulse_delay[15:1];
   assign quarter_pulse_delay = pulse_delay[15:2];
   assign valid_interval      = ((delay > quarter_pulse_delay) & (delay < half_pulse_delay) | clear_line);

The only part remaining is to make certain that exactly one logical change occurs in a given SCL clock phase.  Since the interval may be long enough that several signals could be sent (and we only weant to send one) we will include a block after a signal has been sent (SDA state has been changed logically) that won't be released until the SCL state changes.


  if (scl == scl_p) begin
            // scl remains high or low
            if (delay < pulse_delay) begin
               delay <= delay + 1;
            end
            else begin
               if ((high_block | low_block) && !block_p) begin
                  delay       <= 16'b0000000000000000;  
                  block_p     <= 1'b1;
               end
               else begin
                  // if nothing happened then it's been long enough we know
                  // that we are stretching so signal the line is clear.
                  clear_line  <= 1'b1;    
                  block_p     <= 1'b0;   
                  high_block  <= 1'b0;  // clear block     
               end            
            end   
         end    
         else begin
            // scl went high from low or low to high 
            clear_line  <= 1'b0; 
            block_p     <= 1'b0;   
            delay       <= 16'b0000000000000000; // reset counter
            if (!scl) begin
               high_block  <= 1'b0;  // clear block  
            end
            else begin
               low_block   <= 1'b0; // clear block      
            end       
         end
Now it's simply a matter of handing the interaction with other modules and completing the setup and completion of the signals we wish to transmit.  The way we are going to do this is to build a state machine that keep track of what signal we want to transmit, we are currently trying to transmit, and whether we have completed the process.  It's easier for me to connect multiple modules using an input line for the intent and a return ack to indicate the intended task was completed (when ack is low a new intention can be set, when completed an ack is returned, when the intention is released the ack is cleared) - I used this method very often.

  case (state)
           (`TF_COMPONENT_TWI_XMT_STATE_IDLE): begin
              // delay the release of sda_enable so that just in case we are sending
              // multiple signals in a quick stream, we don't induce unnecessary noise on
              // the sda line this way...
              stretch_high <= 1'b0;           
              if (slipcounter > 0) begin
                 slipcounter <= slipcounter - 1;
              end    
              else if (sda_xmt || (slipcounter == 3'b000)) begin
         // disable the transmitter if we were transmitting high sda because if a
         // slave pulls sda low too early, then we get an arbitration lost alarm.
         // This is better than just disabling sda to send a high signal because in
         // that case we would not detect an arbitration problem at all...
                 sda_enable <= 1'b0; // disable trasmitter                                             
              end
                     
              // process or reset flag back when signals are cleared
              if (!xmt_start && !xmt_stop && !xmt_data0 && !xmt_data1) begin
                 xmt_ack <= 1'b0;
              end
              else if (!xmt_ack && xmt_start) begin
                 state        <= `TF_COMPONENT_TWI_XMT_STATE_START;            
              end
              else if (!xmt_ack && xmt_stop) begin
                 state <= `TF_COMPONENT_TWI_XMT_STATE_STOP;             
              end
              else if (!xmt_ack && (xmt_data0 || xmt_data1)) begin
                 state <= `TF_COMPONENT_TWI_XMT_STATE_DATA;            
              end                    
           end
           (`TF_COMPONENT_TWI_XMT_STATE_START): begin
              if (oktosetup_start) begin
                 sda_xmt      <= 1'b0;
                 sda_enable   <= 1'b1;
                 stretch_high <= 1'b1;         
                 state        <= `TF_COMPONENT_TWI_XMT_STATE_DATA_FIN;      
              end
           end    
           (`TF_COMPONENT_TWI_XMT_STATE_STOP): begin
              if (oktosetup_stop) begin
                 sda_xmt      <= 1'b0;
                 sda_enable   <= 1'b1;
                 low_block    <= 1'b1;
                 stretch_high <= 1'b1; // extend setup time
                 state        <= `TF_COMPONENT_TWI_XMT_STATE_STOP_FIN; 
              end
           end
           (`TF_COMPONENT_TWI_XMT_STATE_STOP_FIN): begin
              if (oktofinish_stop) begin
                 sda_xmt      <= 1'b0;
                 sda_enable   <= 1'b1;   
                 xmt_ack      <= 1'b1;
                 high_block   <= 1'b1;
                 stretch_high <= 1'b1;
                 state        <= `TF_COMPONENT_TWI_XMT_STATE_IDLE; 
              end
           end    
           (`TF_COMPONENT_TWI_XMT_STATE_DATA): begin
              // setup sda to represent the data then wait until we can change sda again.
              if (oktosetup_data) begin
                 sda_xmt    <= xmt_data1; // covers both 0 and 1 xmt
                 sda_enable <= 1'b1;
                 low_block  <= 1'b1; // block forces finish to be on the next pulse             
                 state      <= `TF_COMPONENT_TWI_XMT_STATE_DATA_FIN;
              end
           end
           (`TF_COMPONENT_TWI_XMT_STATE_DATA_FIN): begin
              stretch_high <= 1'b0;
              // finish sending data, signal back data was sent and delay sda release.
              if (oktofinish_data) begin
                 // don't block here since it's ok to send more data
                 xmt_ack     <= 1'b1;
                 slipcounter <= 3'b111; // stop unneded noise if pumping fast
                 state       <= `TF_COMPONENT_TWI_XMT_STATE_IDLE;
              end
           end
         endcase // state machine logic 
This leads to a design considerably more complex than the receiver, but it not really too difficult to understand.


The whole verilog file containing initializations, input and outputs for the module is here.

Thursday, February 24, 2011

Detecting twi signals...

One of the most common ways to use an open collector bus is to allow multiple different devices to communicate with each other without a serious risk of electrically damaging each other.  Every device has physical limits of the amount of energy it can supply or drain safely - it's not a problem when one device is in control of the current but when multiple devices are active at the same time bad things can happen.  An easy way to remove this risk is to have a bus where the only source of current is limited to design by a resistor and only allow devices attached to the bus to sink the current (connect it to ground).  This way if multiple devices are sinking the current at the same time there is no risk of physical damages (the resistor limits the maximum current on the bus) and only if all of the connected devices are in high impedance mode will the bus achieve a high state (again fed current by the resistor).

The only problem here is now one of deciding in what order devices should listen to each other and what order devices should speak...  One of the most common communications protocol here I2C (there are many other variants with alterations in the protocol for different design reasons).  Two wires are used to communicate - one for a clock (SCL - serial clock line) and one for data (SDA - serial data line) and these connect to every device.  The key is that at any given time there is only one physical device that has logical control of the bus and that device supplies the clock (SCL).  If you think about it there are only 2 timing signals (positive and negative edge) on each of the SCL and SDA lines which means that there are 4 effective signals that can be transmitted over the pair.

Since we know that we need to be able to transmit a 0 and a 1 for binary data that leaves us with two other signals for controlling the logical state of the bus.  In convention we will say that if the SDA line stays consistent for the entire period that the SCL line is high (between sequential positive and negative edges) then the state of SDA during that time will represent the 0 or 1 we are transmitting.  If the SDA line changes during the period that the SCL line is high (between sequential positive and negative edges) then the direction of change in SDA (posedge for stop or negedge for start) during that time will represent the logical beginning or end of a logical set of 0's and 1's that belong together.


So, if we want to determine what signals (0,1,start,stop) exist on the bus we need to build a little detector module.  This is rather straightforward in verilog - if we keep track of the previous states of sda and scl and compare them to the current states we can easily determine which of the four possible signals have occurred.  When we see one of these conditions, we will set the appropriate output signal (rec_start, rec_stop, rec_data0, rec_data1) high.

     scl_p <= scl; // remember previous state
         sda_p <= sda; // remember previous state
        
         if (scl && !scl_p) begin
            // we have the positive edge of a pulse
            sda_f <= sda;  // remember sda state at front edge of pulse
            flag  <= 1'b1; // set flag - we saw the front edge             
         end    
         else if (!scl && scl_p && (sda_f == sda) && flag) begin              
            // both high or both low on neg edge of scl
            rec_data0 <= !sda;  
            rec_data1 <= sda;   
            flag      <= 1'b0; // reset flag       
         end
         else if (scl && scl_p) begin
            if (sda && !sda_p) begin
               rec_stop <= 1'b1;             
            end
            else if (!sda && sda_p) begin
               rec_start <= 1'b1;  
               flag      <= 1'b0;
            end
         end
Since it's important to know when the signal occurred (they only exist for a moment) we will clear the signals on the next system clock cycle.

         // clear so they last only 1 cycle
         if (rec_start) rec_start <= 1'b0;
         if (rec_stop)  rec_stop  <= 1'b0;
         if (rec_data0) rec_data0 <= 1'b0;
         if (rec_data1) rec_data1 <= 1'b0;
The complete verilog file is here and contains the initializations, inputs and outputs for the module.  Overall it's relatively simple and straightforward to implement (but simple and straightforward is often good).

Wednesday, February 23, 2011

When you don't want a clock... (tristates)

There are times when you don't want a to use a system clock in a design.  Let's say that you have a signal that is dependent on only logic and an input signal - you need the output to be changed when the input signal changes rather than being changed by the timing of a system clock.  This is typically used when having a part of a design not restricted by the clock allows the rest of the circuit to meet timing requirements by shortening the levels of logic in the network or when there is an event that occurs outside the clock domain (like an external device that indicates when signals are valid or interrupts should be handled).

The example we will use here is handling a pair of tristate lines used for serial communication on an open collector bus.  Tristate lines have 3 possible states b0 or b1 or bz where 0 is low, 1 is high and z is undefined (high impedance).  In an open collector format we will use a resistor to constantly pull the line high; however if a device connected to the line asserts a 0 (grounds the line) then the resistor will not be strong enough to keep the line high and all the other devices on the line (which is a bus when many devices are connected to the same line) will see the line as a 0.  If no device is grounding the line then the line will be seen as high (1) by all the devices connected to the bus.  If a device is in a high impedance state (z) then the connection to the bus will not draw or supply enough current to affect the state of the bus line. 

Since we don't have an way of knowing when any other device is going to assert a 0 (or will release that asserting and let the bus go high again) it doesn't make sense to have this circuit dependent on a clock.
For our black box here we will have scl and sda (serial clock and serial data) line that connect to the open collector bus.  For inputs we will also have enable and xmt pairs for each of the bus lines.  The stretch signal is going to be used later but will allow us to force the scl line low (the reason for this will be explained later). Because we don't know what other devices might be doing with the bus we are also going to include status lines that will indicate where or not the effect we wish to have on the bus actually occurs (think about what would happen if we wanted to transmit a 1 on an open collector line that a different device was driving low by grounding it...

This is a simple thing to do, but the verilog design a bit different than a clocked circuit. 

   // tristate handlers - open drain, enable and xmt 0 pull low else let line float                      
   assign sda        = (sda_enable && !sda_xmt) ? 1'b0 : 1'bz;
   assign scl         = ((!scl_enable && stretch) || (scl_enable && !scl_xmt)) ? 1'b0 : 1'bz;

   // status handlers - true if transmitting and line does not match intent
   assign sda_status = (!sda_enable || (sda_enable && (sda == sda_xmt))) ? 1'b0 : 1'b1;
   assign scl_status  = (!scl_enable || (scl_enable && (scl == scl_xmt))) ? 1'b0 : 1'b1;

The assign statement just means that the logic is always going to apply so that as soon as the state of an element occurs it will affect the output signal (clocked circuits us "<=" to connect to the output and continuous circuits use "=").  When there is a statement in the format of xxx ? yyy : zzz it just means that if yyy is true (high, 1) then xxx = yyy and otherwise xxx = zzz.  Here that would mean that if sda_enable is high and sda_xmt is low then drive sda low (0) by grounding the line but otherwise maintain a high impedance connection to sda (don't affect it's level).  The scl assignment is very similar, but also includes the stretch signal in the logic.

The status lines are binary and not tristate but I used a similar pattern for the assignment.  If a line is enabled (scl_enable or sda_enable - meaning that we intend to transmit) and the bus matches what we intend to transmit (scl_xmt or sda_xmt signals) then output a low state(0) but if there is not a match and we are intending to transmit then output a high state.

The overview of the design ends in buffers rather than flip-flops (because we are connecting to a tristate bus and it's really a disconnect or ground type of output - although you can have non-clocked circuits end in flip-flops).

While sda and scl have no interconnections here and could easily be in separate modules I have placed them together because they are logically connected in the design.  The whole verilog file with the module declaration, inputs and outputs can be found here.

Monday, February 21, 2011

Making a little clock...

Instead of a simple clock that periodically changes from one state to another at a predictable rate, what if we need something that can be adjusted and modified dynamically... we can simply combine logic with a currently available clock signal to create whatever output clock signal we desire.

In this example we will start by creating a black box (we don't care about what's hidden in the box yet) to describe the circuit we want to build.  Here we know that we want a special clock as an output (so it goes on the right).  On the right we will put inputs for our base clock, a reset line (to put our black box into a defined state), lines to stretch our clock output signal in either a high or a low state, and a set of 16 lines to define a delay (which will determine the fundamental relationship between the input and output clocks).  The delay is going to allow us to slow down the output clock relative to the input clock.


Now that we know what we want to make we can do the fun part of filling in the black box to make it work.  There are always many ways to implement a particular problem and while some solutions are "better" in a particular design (perhaps because they run faster or make more efficient use of the hardware available) it is always important to remember that the important thing is that it works (that it does the "black box magic" that solves the problem.  For this reason, people tend to find comfortable patterns of designing things that can be very personal - it makes sense to them, it's easy for them to understand and debug, but maybe not so easy for other people to understand.  It is for this reason that many groups and business will enforce specific coding practices that make it easier for a group to maintain designs, but also makes it suboptimal for every individual working to solve a problem.   Personally I think the best solution is to make certain that whatever you design is commented well enough that another (not just yourself in the future) can understand the design problem and how you are approaching the solution.  It's probably almost always better to spend the time to over comment than to assume that a reasonable person likely to see the design should simply understand commonly used elements.

The first thing we need to do is define the conditions when we want the output signal to change.  In this case we are going to use the positive edge of the base clock.  (Verilog makes more sense to me as an HDL language than VHDL so that's what I will use here - included verilog excerpts will be colored green here.)

always @ (posedge clock) begin
end
This simply means that we want everything between the "begin" and "end" will be evaluated whenever the "clock" signal is rising from low to high.  Next we will add conditions for what do do if the reset line is high... The reset line needs to be dominant (it needs to override any other logic) and should set all of the internal logic into a defined state - so that we can use the reset line to make certain our "black box" will be in a state that we know completely.  If we don't do this then there will be no way to put our design into a state that we completely understand after the power is applied.


 always @ (posedge clock) begin
      if (reset) begin
         // reset state and delay
         delay                       <= delay_16bits;
         delay_stretch_low  <= 1'b0;
         delay_stretch_high <= 1'b0;    
         clock_out                <= !stretch_low;        
      end           
      else if (!reset) begin
      end 
end          

So when reset is high, delay will be set to the values on the the delay_16bits inputs, clock_out will be set to the opposite of the stretch_low input, and delay_stretch_low and delay_stretch_high will be set to 0.  When reset is low the rest of our design will be active between the next begin/end pair.  What we will do when the system is active (reset is low) is to first look at the value of delay... If delay is 0 then we will adjust the clock_out signal and set the delay again.  If delay is greater than 0 we will decrease delay by 1.  In this way delay allows us to slow down the rate that clock_out changes relative to our base clock.  For example, if our base clock is 50MHz and we want clock_out to be 100KHz then we want a ration of 500 between the two -> since half the time the clock_out will be low and half the time the clock_out will be high in a single cycle we need to spend half the time (250 clock posedges) in each phase -> since it takes 1 clock posedge to change the state of clock_out we need to decrease the delay 249 times between the changes in clock_out... Therefore to get a 100KHz clock_out from a 50MHz base clock we need to set the input delay_16bits to 249.


   if (delay == 16'b0000000000000000) begin
            // The only time we have something interesting to do
            // is when the delay hits 0.
            if (clock_out) begin
               // If we are high
               clock_out          <= (stretch_high | delay_stretch_high);
               delay_stretch_high <= 1'b0;                    
            end
            else if (!clock_out) begin     
               // If we are low
               clock_out          <= !(stretch_low | delay_stretch_low);
               delay_stretch_low  <= 1'b0;
            end
            // reset the delay counter
            delay              <= delay_16bits;
         end // if (delay == 16'b0000000000000000)
         else begin   
            delay <= delay - 1;
            // extend an unfortunately timed high stretch
            if (stretch_high) begin
               delay_stretch_high <= 1'b1;            
            end
            // extend an unfortunately timed low stretch
            if (stretch_low) begin
               delay_stretch_low <= 1'b1;             
            end
         end // else: !if(delay == 16'b0000000000000000)
 We also wanted to stretch the clock_out either low or high depending on those input lines... It's easy to do this when the clock is being changes by setting clock_out to stretch_high if clock_out is currently high or to !stretch_low if it is currently low.  The problem is that if the stretch_high or stretch low lines go high when we are just counting down delay, there will be no effect on clock_out.  Sometimes that may be what you want in a design, but here I want the next phase to be stretched even if the stretch signal occurs intermediately when the clock_out line is stable.  The easiest way is to use registers (delay_stretch_low or delay_stretch_high) to monitor the signals.  These can only be set when delay is being decreased and will always be reset when delay is 0.  By combining these with the current stretch line states using a simple or gate when we set clock_out the behavior of clock_out now matches what we intend.

 This is a small part of the design that the verilog describes.  One the right is the single flip-flop that generates clock_out.  The only signals that make it on are 1 clock line (which is out base clock), 1 set line (which sets the initial state on reset), and 1 logic line that indicates whether to change the output state.  Every clocked circuit come down to a simple flip-flop.

If we look at the whole design you can see the single flip-flop for the output on the right and the input lines on the left, but in the middle we see quite a few elements.  The part that looks kind of like a hairbrush (many red lines next to a vertical bar with a single red line leading out) is the evaluation of whether delay == 0.  The long vertical line of flip-flops are the 16 bits of delay.  The other little pieces complete the logic (setting values, comparing values, mixing signals).

Overall this is a small and simple design, but it's useful and makes a nice beginning example.  The whole verilog file contains more elements that define a module (inputs, outputs, initial states) can be found here: http://code.google.com/p/tanukifu/source/browse/trunk/FPGA/TWI/BASE/tf_component_scl_variable.v

Saturday, February 19, 2011

Clocks and logic...

When one has a "clocked" circuit the only important thing to know is that the clock determines the moment in time that the logic component determines the output.  While the logic component may use many separate signals internally, there will always be one signal that represents the output of that logical network and so in all clocked circuits it comes down to mixing a single clock source and a single logic source into a single output... In a simple case one could have a logical element that mirrors the clock signal when the logical source is high and drives the output low when the logical element is low.   If the green (logical) signal were shifted slightly to the right in this case, the red (output) signal would still look exactly the same.  Only if it was shifted enough to the left or the right such that the first or third posedge overlapped the high level of the logic source would the output be different.

One could also set things up so that the output simply mirrors the logic source but changes can only happen on when the clock signal is rising (posedge).  If the green (logical) signal were shifted slightly to the right in this case, the red (output) signal would still look exactly the same.  Only if it was shifted enough to the left or the right such that the first or third posedge overlapped the high level of the logic source would the output be different.

There isn't really a way for this to be a correct set of relationships because the the relationship is not consistent... While it is possible to have the output change based on both the increasing and decreasing edges of the clock for the same logic source, the relationship must be the same in all cases.  If you see this type of relationship, it is likely that what you think is the logic source really is an intermediate part of a larger network (or you have a circuit that is not completely logical driven, or there is a physical problem with your circuit).

In essence the actual relationships between logical elements and clocks can be reduced to a very limited set of output signals (which is good because things would be much harder if they were not - on each of the positive and negative edges of the clock (2 possibilities) the logic(high or low, 2 more possibilities) can be used to determine whether the output is high or low)... So, every single output channel can be modeled by a flip-flop, an input (logical network output), and a clock... All the complexity comes in deciding what the logical network looks like for that single output.  In practical situations it's not so easy to think this way for complex problems and so we use modeling and programming languages that allow us to design these logical networks using various collections of other networks that have some type of specific meaning to us... (which we will see later)

Friday, February 18, 2011

The Tanukifu project...

Lately I've been a bit busy with some little projects (and so there hasn't been a lot of my normal social activity)...

Most (if not all of my life) I've tried to learn something new every day.  For whatever reason I seem to be the happiest when I can say at the end of the day that I've been changed the way I look at the world - that I have a better understanding of who I am...

Today I have finally decided to start putting one of these recent projects online.

The goal of the Tanukifu project is to build a flexible and open set of tools to explore system development for purely educational/academic reasons. While high performance IP modules are available from many vendors (and are often the correct business choice), these are often non-ideal for learning the "how and why" behind the designs or the implementations. Therefore the goal here is to take the specifications for a given device or the raw concepts for accomplishing a particular goal and then create an open source implementation that allows one to use that given device. It is not expected that the project will lead to the fastest/smallest/most efficient solution, but rather that it will lead to better a understanding of how and why things work (something that really only occurs when people can play around and make mistakes along the way).

Right now I've set up a code repository for the project at http://code.google.com/p/tanukifu/  I've only made a tiny part of the project available (an I2C/TWI controller in verilog) - but on it's own it's something that could be useful to someone else.  Over time I will add some posts that explain the way the part work and many more things will be added... Overcoming that first inertia is the hardest part...

Thursday, February 17, 2011

Time, clocks and getting work done...

One of the most fascinating things about nature is time... at it's essence time is why work can be done, why the world has order, why meaning can be perceived... without time there is no meaning...

If one wants to do work then something about the relationship of objects in nature has to change... (that's really all work is).  In order for the relationship between objects to change one has to be able to discriminate between two different states and to know the order of those states.  In the simplest case we can choose the two states as being 0 or 1 (it doesn't matter what the actual meaning of 0 and 1 are - they could be full and empty, hungry or satiated, hot or cold, having a positive charge or not - as long as we can tell the difference between them, as long as they can be measured they will "work").

If we look at what meaning we can derive from being able to measure two states of something, we find that there are four possible conditions: 1) the states over time could be stable and high 2) the states over time could be stable ad low 3) the states could change from low to high and 4) the states could change from high to low. 

While the two stable conditions (high or low) could be used to consider whether or not something else could/should occur, they can not be used to do any work themselves... these form logical states.  The transition states (either going from high to low or low to high) are more interesting though - since here we can measure time and that means that if we use these conditions as signals and since the rising and falling conditions must occur in a an interleaved sequential order (one can not have a rising edge following a rising edge) we can do work.

Clocks (no matter what kind of mechanism or power source) always measure time.  It is the flow of energy from a higher potential to a lower potential or from a more ordered state to a less ordered state that allows work to be done.  In electronics we often use crystals that oscillate when voltage is applied and when used as part of a resonant circuit a predictable number of changes in states over time occurs.  It doesn't really matter that much exactly what the rate of oscillations in the natural resonator is because we can combine those transitions (the positive and negative edges) with other logical conditions to achieve just about any secondary pattern of signals we desire (which we will see in the next post).

The key concepts here are that in order to do work of any kind, we must be able to measure not only different states of something (logical conditions), but also the sequential changes (timing conditions) between those states.