Since the writing of my original article on using CPLD's in robot designs, many have posed the idea that this function or that function could probably be implemented with such technology. The last article offered quite a solid introduction with several simple examples and yet only a few seem to have picked up on these ideas and used them to implement working programmable logic on their own robots.
As such, this follow on article is intended to show two full implementations of TPU style functions that apply directly to personal robotics. I've chosen TPU style functions, as they seem to generate the most questions across the list server. It has also been nice to see that the few individuals that have taken on the CPLD design scheme have gone far above and beyond the primitives I posted originally.
|Do you need a refresher before digging in? Refer back to one of my previous articles on CPLD's that has much more basic getting started type information.|
The two functions covered here in are PWM output and PWM capture. Both of these have come up several times and are functions well suited to external logic. This is so due to the high software overhead required to run these tasks on a standard microprocessor that does not have dedicated hardware to handle these tasks.
Before we get to the two function implementations I'll take a few minutes to cover in further depth a few topics that several have come back to me with questions about following the original article. These queries have centered on the concepts of design fitting and resources.
There are key factors in choosing or sizing the CPLD that will be required for a particular project. In the case of the PWM function one must evaluate the number of flip-flops required to implement the registers and counters. Based on this information one can then choose an acceptable device with which to implement the given logic. There is however a catch to this that the reader is cautioned to watch for. Specifically, let's look at the line below:
Example Listing-1PWM_OUT.AR = (COUNT[9..0].Q == PULSE_REG[9..0].Q]);
Once compiled by a smart reducing compiler (more on this to follow, below..) the final input equations to the asynchronous reset input of the flip flop will look like the following:
Example Listing-2!PWM_OUT.AR = (!CT9.Q & P9.Q) # (CT9.Q & !P9.Q) # (!CT8.Q & P8.Q) # (CT8.Q & !P8.Q) # (!CT7.Q & P7.Q) # (CT7.Q & !P7.Q) # (!CT6.Q & P6.Q) # (CT6.Q & !P6.Q) # (!CT5.Q & P5.Q) # (CT5.Q & !P5.Q) # (!CT4.Q & P4.Q) # (CT4.Q & !P4.Q) # (!CT3.Q & P3.Q) # (CT3.Q & !P3.Q) # (!CT2.Q & P2.Q) # (CT2.Q & !P2.Q) # (!CT1.Q & P1.Q) # (CT1.Q & !P1.Q) # (!CT0.Q & P0.Q) # (CT0.Q & !P0.Q);
It may take you awhile to recognize the fact that the two code examples (1 & 2) above are identical in logical content. The importance of seeing this is that the code one types such as that in example listing #1 above will be converted by the compiler to a much simpler form that can be directly implemented with AND & OR operators. Another way to write this by hand would look like the following and may be a bit easier to understand the comparison of two 10-Bit numbers:
Example Listing-3PWM_OUT1.AR = (((!CT9.Q & !P19.Q) # (CT9.Q & P19.Q)) & ((!CT8.Q & !P18.Q) # (CT8.Q & P18.Q)) & ((!CT7.Q & !P17.Q) # (CT7.Q & P17.Q)) & ((!CT6.Q & !P16.Q) # (CT6.Q & P16.Q)) & ((!CT5.Q & !P15.Q) # (CT5.Q & P15.Q)) & ((!CT4.Q & !P14.Q) # (CT4.Q & P14.Q)) & ((!CT3.Q & !P13.Q) # (CT3.Q & P13.Q)) & ((!CT2.Q & !P12.Q) # (CT2.Q & P12.Q)) & ((!CT1.Q & !P11.Q) # (CT1.Q & P11.Q)) & ((!CT0.Q & !P10.Q) # (CT0.Q & P10.Q)));
Why would one take the time to figure out how many AND & OR operators are required for a particular function? The answer applies directly to the way that the silicon within a particular device is laid out. With respect to the calculation at hand we are evaluating what is called the fan in architecture or width of a logic block. This becomes a key point of contention in choosing a particular silicon implementation when different devices within a family have different numbers of fan in's. The particulars of the ISPLSI1016 logic requirements that comprise a generic logic below are presented below:
Figure 1:Generic Logic Block
Were one trying to fit this logic to a ISPLSI1016E device it would take up far more logic block resources due to the limitation of 16 OR'ed input lines per logic block. The number 16 comes from the image above labeled 0..15 across the top left of the image. Note that each of these sixteen feed horizontally into 4-bit wide OR-gate. So this device would require more than one logic block to carry out this operation. Additionally, the part of the equation that must be fit to alternate logic has a differing time delay than the rest of the logic handled by the local logic which can cause jitter depending on the output mechanism.
The a-fore mentioned 'feature' is common to all CPLD's and should be considered by the design engineer when selecting a target device. To contrast this, ponder for a moment the input structure of the ISPLSI3000 series part. This target has a 24 bit wide OR input path which could easily handle the equation above in a single generic logic block.
The two examples included here will not have product term enable or product term clock considerations, but with other more bus-oriented functionality's these are key design limiting functions. In all CPLD's irrespective of manufacturer logic is allocated in discrete groups with so many flip flops with 'X' wide inputs and 'Y' sized AND arrays etc for inputs and outputs. Commonly there are a certain number of system level clocks available to latch or clock flip-flops. With most devices however, there is only one product term clock (clock that is generated by logic equations) available for one grouping of logic. If you require more in your design the logic must be broken out into separate blocks and thusly tie up more design resources. The same proves true for output enable signals, (NAME.OE), used to drive pins that can be tri-stated or have their output drivers turned off. This again comes from the fact that most logic groupings only have one product term output enable per logic block.
Wondering how you are going to work with a surface mount device like the one shown above, left? Refer to the article I wrote some time back dealing with just such issues.
As you can see in the photo above these parts are not small. Shipping in the PQFP208 package the ISPLSI3160 that I'm using in some of my newer designs contains 7000 gates and 320 registers. This may be overkill for many of the simple robot applications your are contemplating but as follow on articles will show as I introduce hard drive controllers, vision systems, etc. the reader will begin to appreciated the size and power of these devices.
These 208-pin PQFP packages can even be used successfully with 2-layer boards produced by AP-Circuits. Five boards produced each spring at the University of Florida's electrical engineering dept feature ColdFire Processors using just this combination.
Now that we've reviewed some of the considerations that will determine if the functions we are about to discuss will fit into the particular logic device you have on hand, lets proceed to the actual design.
The PWM output function requires input from a microprocessor or other digital device that can specify a 10-bit number representing the duty cycle. I've chosen 10-bit accuracy for this example due to the timing and accuracy the design required. With a design frequency of approx. 22KHz a quick calculation will lead to the desired crystal frequency for the application. Choosing a commonly available off the shelf oscillator close to the value we need and working backwards through the equation we get:
Working through equation 1 above with the off the shelf availability of a crystal of 22.1184MHz we find a target PWM output operating with a frequency of 21.6KHz and a specified accuracy of 10 bits. 21.6KHz is extremely close to 22KHz, our initial target. As a further thought exercise, weigh these performance parameters against what your current microprocessor design can handle. Think about whether or not your software-based solution could keep up when the system parameters are increased by ten fold. A change of the crystal frequency is changed from 22MHz to 220MHz? To give on the argument a bit, this would also require a change to a Cypress or other target.
Above in the section on Design Resources we discussed the silicon resources of a target device based on the logic that the functions to be fit required. For this design all of the logic can be fit into 13 generic logic blocks within an ISPLSI3000 series device. Since the ISPLSI3160 device has 80 generic logic blocks 6 full PWM functions could be implemented on this single chip. However, there is a further economy of scale in that the input pins and the registers set up for the 10-Bit counter function do not need to be fully re-duplicated and as such many more PWM's could be fit into this part. The only limiting factor keeping the silicon area down, would be the requirement that they all drive at the same frequency. However they may each have independent duty cycle control, just the thing for a mobile robot.
More realistic to amateur robotics however, the user would more likely implement two PWM functions, two Encoder functions, that I'll present next month, and a few other functions to round out the processor intensive software functions. These more realistic requirements of a small robot as well as a hard disk interface, vision system and a few other functions that will come in follow on articles should be enough to make the reader re-think using this technology.
With a target device of ISPLSI3160E chosen, the next task is to define the number of input and output pins required. In this implementation, I'll be hooking up 10 lines of my processors 16-bit wide data bus, the read/write (R_W) line, a chip select (L_CS) line and of course my PWM output pin (PWM_OUT). These connections are clearly visible at the top of the AHDL module presented below in Example Listing-3.
Next I define the register requirements of which there are three:
10 Bits as a compare against register latched directly from the data bus. 10 Bits set up as a free running counter designed to wrap around at approx. 22KHz (ref. equ 1). 1 bit determining the on/off status of the PWM output bit.
So now that we've identified the data storage requirements lets take a moment or two to review the theory of operation of the code that will then follow.
The AHDL script sets up a free running ten bit counter that will roll over once a count of 1024 (2^10'th) is reached. This counter is run in a variable called COUNT that is made up of the bits CT9,CT8,..CT0 grouped together. The control register called PULSE_REG is made up of bit P9,P8,..P0 again, grouped together. This register is loaded from the lower ten data bits DAT9,DAT8,..DA0 directly from the processor data bus when both the read/write line (R_W) is low and the active low chip select line (L_CS) are active.
Once the 10-Bit register PULSE_REGISTER has been loaded from the uProcessor's data bus the functionality can be described by the following rule base:
If current count = 0 turn on the PWM output. If current count = value stored in PULSE_REG then turn off PWM output. Increment counter. Counter overflow will auto-wrap to zero.
In programmable logic not tied up within state machines or gated by registered logic, one concept that is hard for most beginners is the idea of concurrency. The issue here is that all-logical steps are happening concurrently. Unlike a computer program, there is no dependency on the order of lines being typed. In a 'C' function, lines are executed sequentially. In a CPLD, combinatorial logic like the function we are developing here are executed in parallel with only the exception of the counter as it is 'registered' by a clock.
Now we've reached that point where it's time to look at the actual code required to implement the PWM function, enjoy:
Example Listing-4MODULE pwm_1 TITLE 'supports pwm output to ten bits' PWM_OUT PIN ISTYPE 'REG_D,BUFFER,KEEP'; R_W PIN; L_CS PIN; YO PIN; DAT0 PIN; DAT1 PIN; DAT2 PIN; DAT3 PIN; DAT4 PIN; DAT5 PIN; DAT6 PIN; DAT7 PIN; DAT8 PIN; DAT9 PIN; NCK NODE; P0 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P1 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P2 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P3 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P4 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P5 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P6 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P7 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P8 NODE ISTYPE 'REG_D,BUFFER,KEEP'; P9 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT0 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT1 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT2 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT3 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT4 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT5 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT6 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT7 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT8 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT9 NODE ISTYPE 'REG_D,BUFFER,KEEP'; PULSE_REG = [P9,P8,P7,P6,P5,P4,P3,P2,P1,P0]; COUNT = [CT9,CT8,CT7,CT6,CT5,CT4,CT3,CT2,CT1,CT0]; DATA_BUS = [DAT9,DAT8,DAT7,DAT6,DAT5,DAT4,DAT3,DAT2,DAT1,DAT0]; H,L,X,Z,C,U = 1,0,.X.,.Z.,.C.,.U.; PLSI PROPERTY 'ISP ON'; PLSI PROPERTY 'PULLUP ON'; EQUATIONS COUNT[9..0] := COUNT[9..0] + 1; COUNT[9..0].CLK = [YO,YO,YO,YO,YO,YO,YO,YO,YO,YO]; PWM_OUT.D = 1; PWM_OUT.OE = 1; PWM_OUT.CLK = (COUNT[9..0].Q == [0,0,0,0,0,0,0,0,0,0]); PWM_OUT.AR = (COUNT[9..0].Q == PULSE_REG[9..0].Q); NCK = ((!R_W) & (!L_CS)); PULSE_REG[9..0].D = DATA_BUS[9..0]; PULSE_REG[9..0].CLK = [NCK,NCK,NCK,NCK,NCK,NCK,NCK,NCK,NCK,NCK]; END
Above, in the first section, we compared how the line in the code above that compares the 10-Bit counter to the 10-Bit registered value gets converted to a simpler equation consisting of only AND & OR operators for eventual fitting in the target silicon. I made the off hand quote that this function assumed a good compiler to get the output described in example listing - 2.
What do I mean by a 'good' compiler? Well, as it turns out, the code I present above is the conjugate of the more complex form presented below. I've only presented the first 24 lines of the 1024 lines of logic required to fully implement the inverted or conjugate logic shown in code listing-2, above. It is worthy to note that the form show below is the standard output of some compilers and will not 'fit' into a small device. One way around this is to explicitly write your logic down to the AND & OR level your self. Another way is to evaluate fully, the tool set you are using by building a few samples you've written and reviewing the output. A sample of the 1024 line function follows:
Example Listing-5PWM_OUT.AR = (!CT9.Q & !CT8.Q & !CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & !P9.Q & !P8.Q & !P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # CT9.Q & !CT8.Q & !CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & P9.Q & !P8.Q & !P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # !CT9.Q & CT8.Q & !CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & !P9.Q & P8.Q & !P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # CT9.Q & CT8.Q & !CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & P9.Q & P8.Q & !P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # !CT9.Q & !CT8.Q & CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & !P9.Q & !P8.Q & P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # CT9.Q & !CT8.Q & CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & P9.Q & !P8.Q & P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # !CT9.Q & CT8.Q & CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & !P9.Q & P8.Q & P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # CT9.Q & CT8.Q & CT7.Q & !CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & P9.Q & P8.Q & P7.Q & !P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # !CT9.Q & !CT8.Q & !CT7.Q & CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & !P9.Q & !P8.Q & !P7.Q & P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q # CT9.Q & !CT8.Q & !CT7.Q & CT6.Q & !CT5.Q & !CT4.Q & !CT3.Q & !CT2.Q & !CT1.Q & !CT0.Q & P9.Q & !P8.Q & !P7.Q & P6.Q & !P5.Q & !P4.Q & !P3.Q & !P2.Q & !P1.Q & !P0.Q (Lines 11..1024 omitted) Scroll right to see how "nasty" this stuff really is -->
Much the same as the PWM input function above, the PWM capture function is used to measure the width of an output pulse. This function can be useful for watching the return from a sonar device or other such device that outputs a variable length positive going pulse as a value. It is a simple task to change the code over to measure a negative going pulse.
As we've chosen a positive going pulse to capture and the microprocessor that starts the capture may not know if the incoming pulse is high or low due to design speed resource limitations, the function implemented within the CPLD will wait until the input is at a logical low level to begin it's measurement. Additionally, when the function is done measuring the input pulse it will lock out further measurements and send an interrupt request to the microprocessor to indicate it has finished. The lockout function will remain in place until the next measurement has been started by a write to the CPLD from the microprocessor.
A note about jitter and line noise: At times the sensor one wishes to measure has noise on it's sensor output lines. If the designer has not taken time to properly isolate the sensor data over long connections through the use of twisted pair wiring or better yet differential signaling over twisted pair wires, then jitter or noise may be induced. The function presented in the CPLD has two jitter removal mechanisms to detect false starts and false endings.
Front end and Back end pulse jitter caused by noise has incredibly low current drive based on the inducted source and as such cannot sustain much in the way of trigger over time. In order to remove these false triggers, each input is checked once to start, and then checked again as sampling the input draws a bit of current and will tend to make an input that has falsely triggered due to noise disappear after the first sample. In so doing, if the input is inactive on the second sample, the system reverts back to attempting to locate an initial sample. This has the effect of increasing the smallest sample that may be measured by the system however, noise removal is of higher importance in my design and if finer resolution is required, the metrics can be chosen as in the example above by a suitable change in time base. (Oscillator frequency)
Taking a moment to compare the function of 16-Bit wide pulse capture in terms of logic allocation as we've done for the PWM function above we find that only 8 generic logic blocks are required. Again there are several pieces of code, which become redundant when implementing multiple pulse captures in one piece of silicon so that less total silicon can be used. Especially when looking at the clock divider circuitry. In the next section let's look closer at the time base provided by this clock divider circuitry.
In this function, time base is even more critical than in the PWM output function above. Here the system metrics call for a sixteen bit wide count that evenly measures the pulse width to 1/65536'th of a second. Choosing a crystal frequency that divides out easily leads us to a 8.192MHz crystal oscillator, as described in equation-2 below. One can see that with this choice of oscillator either the function will be run in 1/125'ths of a second or the clock must be divided by 125, a somewhat simple operation in a CPLD as long as the divisor is an integer number.
Starting into the design presented in example listing 6 below, review the seven bit counter set up in the variable CK. As you'll notice it's a binary up counter clocked directly from the 8.192MHz input crystal oscillator pin (YO). This counter counts up until a count of 125 is reached %b[1,1,1,1,1,0,1] at which time the asynchronous reset pin on all of the flip flops in the counter mechanism are toggled, CT[6..0].AR. Thus giving a clock that is now dividing down by 125. The lines which implement this part of the function are outlined in example listing 6 below:
Example Listing-6CK[6..0].D = CK[6..0].Q + 1; CK[6..0].CLK = [YO,YO,YO,YO,YO,YO,YO]; WHEN (CK[6..0] == [1,1,1,1,1,0,1]) THEN CK[6..0].AR = [1,1,1,1,1,1,1];
The next task is that the out going clock must go through a full oscillation cycle in this count of 125. This is done by setting up a single flip flop called PK to act as our new clock. It's /Q (!PK.Q) or inverted output is fed back into the D input (PK.D) so that each time the clock pin is pulsed it will toggle. Keep in mind that the clock pins in most CPLD's are edge triggered on the active high going edge of the clock pulse. So to get two toggles per 125 counts we toggle twice during the count. The first at a count of one and the second at a count of sixty-four. This is more than accurate enough for our purposes as the duty cycle of the clock is not as important as the overall frequency.
Example Listing-7PK.D = !PK.Q; PK.CLK = ((CK[6..0] == [0,0,0,0,0,0,1]) # (CK[6..0] == [1,0,0,0,0,0,0]));
Now, the only tricky part left comes by way of when to clock the PK counter and how to start and stop it. This boils down to a list based set of rules that are time dependent. IE they should not run in parallel as combinatorial logic, but rather need to operate in a synchronous fashion with each next step not operating until the previous step has completed. This requires registered logic and a state machine to control it. Think of this one as being closer to the way software operates on your PC.
Remember that although the states within the state machine are synchronized by registered logic (IE they flow like computer software), all of the equations outside of the state machine are running in parallel without bound unless some how tied to the state machine itself.
W_F_START Here the system sits in a wait loop waiting for the microprocessor to issue a write that will start the state machine running.
STRT The microprocessor has started the capture process, but we must wait until the input becomes low so that the process is not accidentally started in the middle of a measurement. Additionally, in this state the pulse capture register or counter PK[15..0]is reset to a value of 0.
JITTER1 This state checks to make sure the input is still high. If it is not, the input from the line above is assumed to be a false trigger due to noise and the system returns to the previous state. If it is still valid the counter is clocked for the first time and the system continues.
CNT The counting state will be remained in as long as the input is high. The counter is continuously incremented by the clock/125 input discussed in the paragraphs above. This state will only exit based on a low reading from the input.
JITTER2 The second of two jitter removal states, this one is set up to catch jitter on the trailing edge will return to the count state above if the input is high, implying noise on the line. Other wise it will cycle back to state W_F_START with the issuance of an active low L_INTERUPT signal to the microprocessor indicating an end of measurement.
HOLDERS Since the value of the registers is unknown at power up, all valid register states should, in good practice, be defined with a return to a know state. External logic with symbolic state machines such as those used in VHDL can be used in AHDL however, this cost in the silicon implementation being converted to a VHDL type implementation. This takes a minimum of two flip-flops for every state and wastes valuable silicon resources.
So that's pretty much all there is to it. The actual code is given below in example listing 8:
Example Listing-8MODULE pwm6 TITLE 'pulse width capture funct' D15 PIN; D14 PIN; D13 PIN; D12 PIN; D11 PIN; D10 PIN; D9 PIN; D8 PIN; D7 PIN; D6 PIN; D5 PIN; D4 PIN; D3 PIN; D2 PIN; D1 PIN; D0 PIN; R_W PIN; L_CS PIN; L_INTERUPT PIN; YO PIN; CAPTURE_IN PIN; CT15 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT14 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT13 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT12 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT11 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT10 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT9 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT8 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT7 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT6 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT5 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT4 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT3 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT2 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT1 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CT0 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK6 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK5 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK4 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK3 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK2 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK1 NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK0 NODE ISTYPE 'REG_D,BUFFER,KEEP'; ST2 NODE ISTYPE 'REG_D,BUFFER,KEEP'; ST1 NODE ISTYPE 'REG_D,BUFFER,KEEP'; ST0 NODE ISTYPE 'REG_D,BUFFER,KEEP'; PK NODE ISTYPE 'REG_D,BUFFER,KEEP'; CK = [CK6,CK5,CK4,CK3,CK2,CK1,CK0]; COUNT = [CT15,CT14,CT13,CT12,CT11,CT10,CT9,CT8,CT7,CT6,CT5,CT4,CT3,CT2,CT1,CT0]; ST = [ST2,ST1,ST0]; DATA_BUS = [D15,D14,D13,D12,D11,D10,D9,D8,D7,D6,D5,D4,D3,D2,D1,D0]; NK NODE; W_F_STRT = [0,0,0]; STRT = [0,0,1]; JITTER1 = [0,1,0]; CNT = [0,1,1]; JITTER2 = [1,0,0]; SIX = [1,0,1]; SEVEN = [1,1,0]; EIGHT = [1,1,1]; H,L,X,Z,C,U = 1,0,.X.,.Z.,.C.,.U.; PLSI PROPERTY 'ISP ON'; PLSI PROPERTY 'PULLUP ON'; EQUATIONS CK[6..0].D = CK[6..0].Q + 1; CK[6..0].CLK = [YO,YO,YO,YO,YO,YO,YO]; WHEN (CK[6..0] == [1,1,1,1,1,0,1]) THEN CK[6..0].AR = [1,1,1,1,1,1,1]; PK.D = !PK.Q; PK.CLK = ((CK[6..0] == [0,0,0,0,0,0,1]) # (CK[6..0] == [1,0,0,0,0,0,0])); COUNT[15..0].D = COUNT[15..0].Q + 1; DATA_BUS[15..0] = COUNT[15..0].Q; NK = (R_W & !L_CS); DATA_BUS[15..0].OE = [NK,NK,NK,NK,NK,NK,NK,NK,NK,NK,NK,NK,NK,NK,NK,NK]; ST.CLK = [YO,YO,YO];; STATE_DIAGRAM ST STATE W_F_STRT: L_INTERUPT = 1; IF (!L_CS & !R_W) THEN GOTO STRT ELSE GOTO W_F_STRT; STATE STRT: L_INTERUPT = 1; COUNT[15..0].AR = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; IF (CAPTURE_IN) THEN GOTO JITTER1 ELSE GOTO STRT; STATE JITTER1: L_INTERUPT = 1; COUNT[15..0].CLK = [PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q]; IF (!CAPTURE_IN) THEN GOTO STRT ELSE GOTO CNT; STATE CNT: L_INTERUPT = 1; COUNT[15..0].CLK = [PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q,PK.Q]; IF (!CAPTURE_IN) THEN GOTO JITTER2 ELSE GOTO CNT; STATE JITTER2: L_INTERUPT = 1; IF (CAPTURE_IN) THEN GOTO CNT ELSE GOTO W_F_STRT WITH L_INTERUPT = 1; STATE SIX: L_INTERUPT = 1; GOTO W_F_STRT; STATE SEVEN: L_INTERUPT = 1; GOTO W_F_STRT; STATE EIGHT: L_INTERUPT = 1; GOTO W_F_STRT; END
Previously, we talked about the concepts of equations that operate concurrently / in parallel vs. those that operate sequentially such as those made up by the state machine. Here you can see a mix of both. Notice that the microprocessor read mechanism is made up of completely combinatorial or asynchronous members and as such is active at any place in time. It is then important to note that in your software design you want to read the return value only from the interrupt service routine. Also, since we did not choose to implement a state machine asynchronous reset, (read comments in state listing table above), during power up a spurious interrupt may be generated before the software on your microprocessor has had time to set up the interrupt tables. As such the reader may want to implement an initialization register that gates the output enable on the L_INTERUPT.OE line until software has had time to configure it.
As stated in the opening of this article one of the driving reasons that lead me down this path of development came from inquiries based on my original encoder article suggesting the use of CPLD's in your next design. A further exploration came when switching to a new high-powered processor family, Motorola's ColdFire processor family. I could sing the praises of this microprocessor until I'm blue in the face, but the hard reality hits home that the trade off for extreme speed, band width capabilities, and large cashe along with a few DSP functions comes the loss of the TPU.
So I've been driven down that road myself, to chew the question, whether it is better to throw on a 68332 as a slave processor with it's rather largish number of associated support components or to write the TPU functions that I needed in a dedicated support CPLD. In an up coming Seattle Robotics Society Encoder article I'll present both the ColdFire design and completely integrated CPLD design or 68332 slave processor design whichever makes the final cut, stay tuned....
The reader may also be wondering how devices with the physical dimensions as those I speaking of here (PQFP208 package) can fit onto a small mobile robot? The two figures below show a board layout that measures only 3.5" x 4" which contains two such 208 pin devices as well as a heck of a lot of other circuitry. The figure is presented to re-assure the reader that it can be done in a small space that meets the needs of mobile robotics.
As discussed above these parts are small enough and manageable enough that they can still be used on double-sided boards produced through AP-Circuits.
Drinking Diet Coke - N - Gettin' it done. - Kenneth
Come visit my web site to check out more on this and other projects:
Max's Little Robot Shop