Soliciting suggestions for SX design.
Updated to fix timing (390nS, not 390uS)
I'm new to SX development (been doing AVR stuff lately), so I thought I'd see if more experienced SX developers have wisdom to share.
My application is a 300-230400 dual UART.· I've dloaded the Dual UART VP, and it looks like with some additions (DTR/DSR/CTS/RTS/300bps/1200bps support), I can make that code work.·
My issue is the need to watch for an asynchronous data request that must be serviced in 390nS.·(@75MHz, 13nS per cycle, I calculate 30 instructions available)
My initial thought was to spin in the main code, watching for the event, but the dual UART code looks like it takes too much of the·30 instruction alottment, meaning I could miss the trigger.
So, I thought about making the trigger the IRQ, and running the dual UART code in the main loop.· Obviously, that means I need to substantially change the VP code and use the SX48/52 timers to keep me accurate.
In the AVR, I'd have dumped both chunks of code in IRQ code, and turned IRQs on in the serial bit bang code (in case the asynch trigger came in while in IRQ code), but I've seen reams of documentation on the SX warning about not having multiple asynch IRQ sources, as they might get lost.
Anyone have other ideas, or can comment on my current idea?· Can anyone comment on why they limited the IRQ stack to one level?·
Jim
Post Edited (Jim Brain) : 10/26/2004 3:23:59 PM GMT
I'm new to SX development (been doing AVR stuff lately), so I thought I'd see if more experienced SX developers have wisdom to share.
My application is a 300-230400 dual UART.· I've dloaded the Dual UART VP, and it looks like with some additions (DTR/DSR/CTS/RTS/300bps/1200bps support), I can make that code work.·
My issue is the need to watch for an asynchronous data request that must be serviced in 390nS.·(@75MHz, 13nS per cycle, I calculate 30 instructions available)
My initial thought was to spin in the main code, watching for the event, but the dual UART code looks like it takes too much of the·30 instruction alottment, meaning I could miss the trigger.
So, I thought about making the trigger the IRQ, and running the dual UART code in the main loop.· Obviously, that means I need to substantially change the VP code and use the SX48/52 timers to keep me accurate.
In the AVR, I'd have dumped both chunks of code in IRQ code, and turned IRQs on in the serial bit bang code (in case the asynch trigger came in while in IRQ code), but I've seen reams of documentation on the SX warning about not having multiple asynch IRQ sources, as they might get lost.
Anyone have other ideas, or can comment on my current idea?· Can anyone comment on why they limited the IRQ stack to one level?·
Jim
Post Edited (Jim Brain) : 10/26/2004 3:23:59 PM GMT
Comments
I'm not quite following your concern. You have to service a data request in 390 us, or .00039 seconds. The chip is running at 75 MHz, eg. a 13 ns cycle time, which means each clock takes .000000013 seconds. When I do the math, I get the following:
.00039 / .000000013 = 29250 cycles per 390 us period. Did you get the service time wrong? In order to have only 30 cycles, you would need to service the data request in 3.9 us, not 390 us. Can you clear up my confusion?
Thanks, PeterM
75,000,000 / 230,400 = 325.5 cycles per interrupt
If you use 3 times oversampling that is 108.5 cycles per interrupt.
I think you could easily do a single uart that way, but thats really tight to do a dual uart (but it could probably be done).
Bean.
I've looked at the VP code, and I think I can change the VP scheduler to run as a regular task. My idea is to:
; poll RTCC overflow flag
; load RTCC with new count
; jump into VP code table
That way, the VP code could still be used pretty much as is, but I'd then use the IRQ to catch the asynch event. The event can be serviced in 3 or 4 instructions, so I think it would place little to no load on the main rs232 tasks.
The problem is, the SX datasheet gives precious little information on the RTCC usage. I can see from the VP scheduler code that the IRQ code starts at $0 (although somewhere on the net stated location $4 is the vector), and it does the jmp table stuff. All VPs end up loading W with 217 (or 108 or whatever), and the retiw. But, that leaves questions in my mind, like:
Where is it documented that RTCC is loaded with W on return? I can't find it in the datasheet at all.
Since you've already expended some cycles doing your IRQ routine, if you load RTCC with 217 again at the end, for example, won't that cause your IRQ handler to be delayed for the next round (In other architectures, if you used a one-shot timer to generate an IRQ, I was always instrcuted to load it again in the beginning of the IRQ handler, to avoid the "walking timebase" issue. I suppose you could subtract the cycles the VP uses from the count before loading W, but I don't see the VPs doing that. I must be missing something.
Jim
Yeah, when I first started playing with the SX I wondered about the whole return value as well. However, it turns out that the SX does just what you want it to do.
If "int_period" is set to 152, and the RTCC is 1:1, then as long as your interrupt code is shorter than 152 cycles (actually, there a few overhead cycles for getting into and out of the interrupt as well), the intterupt period will always be 152 cycles long. It doesn't matter if your interrupt code takes 20 cycles on one interrupt, and 106 on another, the SX does the right thing. Bear in mind that the "-" in front of the interrupt period is critical, since it is the key to having the SX automatically calculate the right value for W to cause the interrupt to remain consistent. Basically it's using overflow math to do figure out the remaining number of cycles.
As for your time issue, I'm just curious what sort of serial device needs a response in 390 ns before a timeout or problem. I would also look closely at the code to determine how much time is spent in the interrupt handler if there is no data coming in or out. While the worst case scenario might be cutting it close, how often is the code in the worst case versus the best case? I often find that interrupt code worst case is "just getting by", but that most of the time, the main code has plenty of cycles to handle its work. As long as you have enough for the worst case, then it only gets better for the best case.
Thanks, PeterM
One thing to consider is that if both external interrupts and the RTCC interrupt are enabled, you will always have the risk of adding jitter to the RTCC interrupt. As a result, the timebase for all VPs will be unreliable. To maintain jitter free operation of VPs, it is uusually better to simply poll the external interrupt sources in the ISR. There is usually some acceptable delay in responding to an asynchronous event. To help with this, the WKPND register can still be used even with external interrupts disabled - it will still show what external interrupts were triggered.
I have copied the following text from my book, and I hope it explains how simple it is to achieve exactly timed interrupts:
Let's assume that we want an interrupt every microsecond at a system clock frequency of 50 MHz. This means that an interrupt must be triggered every 50 clock cycles. To achieve this, it is not sufficient to load RTCC with 256-50 = 206 at the end of the ISR. You must keep in mind that the instructions within the ISR already have taken clock cycles while RTCC was incremented, and three more clock cycles were required to enter the ISR. Another thee clock cycles are "stolen" to return from the ISR.
In order to find out the correct RTCC initialization you would have to sum all these clock cycles, and whenever you make a change in the ISR code, you would have to re-calculate the sum. Things get even more complicated when the execution time of the ISR instructions is not constant. This can easily happen when conditional skips are involved.
Fortunately, there is no need to do all this manually because the SX keeps track of the clock cycles "used" within the ISR. Because the RTCC keeps incrementing after its overflow has triggered the interrupt, its contents at the end of the ISR exactly holds the number of clock cycles "used" so far.
Therefore, we take the number of clock cycles that shall elapse between two interrupts (nC), subtract this value from the current contents of RTCC in order to get the correct initialization value for RTCC.
To understand this, assume for a moment that the ISR did not "use" any clock cycles, and that the call and return do not require any clock cycles either. This would mean that RTCC contains zero, i.e. the "magic value" that has triggered the interrupt. In order to have the next interrupt triggered after RTCC has been incremented for nC cycles, it is necessary to decrement it now by nC. This is why we subtract nC.
In reality, less than nC cycles are required until the next interrupt to be "just in time" because the ISR really did "use" clock cycles. As mentioned before, this cycle count is contained in RTCC and so the correction is done automatically.
There is even more good news: The retiw instruction has been included to do exactly that! Similar to reti, retiw returns from an interrupt but it also adds the contents of w to the RTCC.
In order to store (RTCC - nC) to the RTCC, we just need to store -nC to w before executing the retiw instruction.
To achieve an interrupt period of 1 µs (50 clock cycles), for example, just write the following code:
mov w, #-50
retiw
Congratulations to the SX development team - this "little trick" is one of the features that make the SX so powerful!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Greetings from Germany,
Günther
Reading this, I noticed in para3, it makes the ominous statement about multiple IRQ sources, but in para5, it then notes that if one has MIWU IRQs on and they occur while in the ISR routine, they WILL get serviced immediately following. However, that begs the question of how it behaves if you have 2 MIWU IRQs set, and you are servicing one of them when another one triggers.
Maybe it is just me, but the data sheet seems lacking. The data appears to be mostly there, but the organization seems contrary in places, and the corner cases seem inadequately documented.
I now understand how the adding works, though. If I've used 22 cycles in my ISR, adding -217 to it means the RTCC is set to -217+22 = -195, which is clever.
As for the IRQs, since the one event happens at any time and must be serviced in 390nS, I intend on using the IRQ routine for that, since it is the shortest path, and running the VP UARTs as normal non-ISR tasks. That way, I'm only using 1 IRQ, which I can service in 100nS or so.
As you can see, enabling multiple interrupt sources can get pretty complicated very quickly. There is only one level of interrupt, though if you want to get fancy you could enable interrupts again while in the ISR. This would add the support for nested interrupts, but only complicates the confusion further. To my knowledge, very few people have ever found a need for nested interupts in the SX... simply polling external interrupt sources in a SX design still nets a response time much quicker response time than what is possible with an actual interrupt in other chips.
The serial ports top out at 230K, but the other end (the SX will perform an inteface function) is a 1MHz bus. As a result, the bus will expect valid data (i.e. a read cycle) 390nS following the clock signal going high.
Given the application (a retrofit, where register mapping needs to be maintained), the complexity of remapping a 1655X UART mandated a huge CPLD, not to mention adding another IC. As well, I'm trying to control cost, and a CPLD/UART approach was quite a bit more than just a SX48/52.
Above that, I'm curious if one can utilize a high-performance uC like the SX to replace obsolete or hard to find ICs, like PIAs, VIAs, CPUs, etc.
Jim
See, I think my application is one such example, where nested interrupts would have worked very nicely.
I hum along, the RTCC overflows, and I enter the ISR
Immediately after I determine the source of the IRQ, I re-enable IRQs
I start my 17-22 cycle UART receive routine, but in the middle of that, I get an external IRQ telling me the bus is requesting data.
*I re-enter the IRQ routine and determine the external IRQ happened, so I quickly service it and reti
* that drops me back into my UART VP routine, which continue as normal, doing a retiw at the end
* cannot be done in SX, due to single level stack, as far as I know.
Sure, I can poll the external pin during the UART VP operation, but since I need to catch it pretty much as soon as it happens, I'm left with:
; line of UART code
; test for external IRQ
; line of UART code
; test for external IRQ
; etc.
Which seems hardly workable to me. The idea of flipping the VPs to run as the main task and using the IRQ for the trigger seems the most workable.
I appreciate the information on the MIWU, but I'm curious how you determined that, as I just reread that portion in the datasheet, and I couldn't come to any conclusions. I agree handling one at a time is best.
Jim
If you limit your ISR to just flipping a switch when an external event occurs, you can have as many external interrupts as you like. Then you poll for those switches in the main code, and you do the polling in the order of priority of the external interrupts. You do have to pepper the lower priorty VP code with checks for a higher priorty interrupt flag, and then call the main polling routine when one occurs. The main loop calls the main polling routine and then loops, so the main polling routine returns when it completes an interrupt. If a lower priority interrupt called it for a higher priority one, then it will return to the low priority one, otherwise, it will return to the loop.
It would be great if an ISR could change the address that it is returning to since that would allow us to build our own stack system and not use the internal stack at all.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
---
James Newton, Host of SXList.com
james@sxlist.com 1-619-652-0593 fax:1-208-279-8767
SX FAQ / Code / Tutorials / Documentation:
http://www.sxlist.com Pick faster!
I like that idea, but what happens if the external IRQ fires, I enter the ISR, and then the RTCC overflows? According to the docs, it would appear the RTCC IRQ would be "lost".
I'd love to do it this way, but I discarded it for the above reason.
Jim
But, I'm confused. Since so many resources caution against using multiple IRQs due to the possibility of "lost" interrupts, I inferred that if you were in the ISR and another event occurred, it was lost, pure and simple. What I am hearing today is much better news, as the events would not be lost, just delayed. I can deal with jitter, I just can't deal with lost events.
Now, I'm unclear when you would actually "lose" one (except in the case where you stayed in your ISR so long that the same external interrupt triggered multiple times. However, that is the case in any architecture, not specific to the SX.
Jim
Stephen probably has some other, more clever way to do that.
On the issue of dropping interrupts, the only issue I am aware of (which can be worked around) is reported at:
http://www.sxlist.com/techref/ubicom/sxints.htm I'll copy it here:
Alexey Vladimirov [noparse][[/noparse]avlad at mailbox.riga.lv] says:
Typical interrupt service routine contain the following code:
mov m,#$09 ;Set up to read wake up pending register
clr w
mov !rb,w ;Read WKPND_B register content and clear it after that
...
reti
Each SX instruction in turbo mode executed in a 4-stage pipeline, consisting of the following steps: fetch instruction, decode opcode, execute opcode, write result.
In the example mov !rb, w instruction at the second step simultaneously with instruction decode also read WKPND_B register and at the fourth step clear WKPND_B register.
If another external interrupt on different portB pin occurs exactly at this time (at the 2,3,4 steps of the mov !rb, w execution), SX will loose this interrupt (as the WKPND_B register cleared and, therefore, pending bit for this interrupt also cleared without interrupt processing).
It mean, that it is not possible to use 2 or more asynchronous external interrupts on the SX without loosing some interrupts.
This problem related to all current (as of Jan 2001) SX revisions.
[noparse][[/noparse]To work around this, check the pins by reading the actual port value after the first interrupt.] If you will use polling instead of interrupts - all will works OK. You can also use one external interrupt and poll other pins. However, if you have two asynchronous signals and you need interrupt on both (bridge application, for example), the only possible workaround we found - add some external glue logic (triggers for each interrupt with separate reset from SX port, as in any typical interrupt controller, like 8259) or change from interrupts to polling.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
---
James Newton, Host of SXList.com
james@sxlist.com 1-619-652-0593 fax:1-208-279-8767
SX FAQ / Code / Tutorials / Documentation:
http://www.sxlist.com Pick faster!
There is one more way to check if a RTCC overflow occured (only on SX48/52)... The RTCCOV bit in the T1CNTB register. I got this one added, and this was the only place left to put it!
Concerning your idea of setting up a minimal rtcc timer, I implemented it, but it takes 6 cycles to get in an out of the ISR, add another cycle to increment flag to check in the main code, and some code to test if the ISR was entered due to RTCC or PORTB pins, that meant 11 cycles or so that I could be in the ISR, but not servicing the bus request. It also didn't eliminate jitter, as the main VP code did a snb FLAG, jmp $-1, which introduces some jitter. I could cut down on the delay by checking the pin status while in the ISR, but that meant more code to handle that decision tree, etc.
So, I simply took out all the RTCC ISR code, and replaced the main loop with:
snb RTCC.7 ; check bit 7 of RTCC
jmp $-1 ; not rolled over.
add RTCC,#-int_period
int_period is 54
This looks to provide the same stable timer functionality of the RTCC ISR, but it has no delay on servicing the bus request. The add looks atomic, so I should never get interrupted in the middle of adding -54 to the RTCC count.
Now, 54 clocks doesn't give me much time, but I don't think I need much, and I can switch to 3x sampling if I need to.
I did find an issue with SXSim 1.4.1, as I turned off the RTCC IRQ, and in SXSim, the RTCC quits incrementing in that situation. At least the debugger and SXSim differ in their information.
Jim