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