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.