FDS demo with interrupts
Seairth
Posts: 2,474
Here's a simple FDS implementation that partially uses interrupts and the new timers. As it currently stands, it works reliably up to 460,800 bps (well, up to that rate in PST). If those number hold, this should easily work at 1Mbps once the PLL is working.
Right now, the driver has three major components:
* Edge interrupt to detect the beginning of a start bit.
* WRLONG interrupt to detect new data to be sent.
* A loop that uses CT1 and CT2 to do the bit-banging. When there is neither an active RX or TX, this loop is paused with a WAITINT.
Enjoy!
Edit: Added a second demo that entirely uses interrupts for TX. RX starts via interrupt, then uses timer polling for the rest of the receive period.
Edit: Added a third demo that entirely uses interrupts for both TX and RX.
Edit: Updated all three versions to match 2015-10-29 release.
Right now, the driver has three major components:
* Edge interrupt to detect the beginning of a start bit.
* WRLONG interrupt to detect new data to be sent.
* A loop that uses CT1 and CT2 to do the bit-banging. When there is neither an active RX or TX, this loop is paused with a WAITINT.
Enjoy!
Edit: Added a second demo that entirely uses interrupts for TX. RX starts via interrupt, then uses timer polling for the rest of the receive period.
Edit: Added a third demo that entirely uses interrupts for both TX and RX.
Edit: Updated all three versions to match 2015-10-29 release.
Comments
I haven't tested read yet, but write is working great!
Go for it! Note that, at the moment, this is not as flexible as the P1 FDS. It's hardwired to 8N1 and it doesn't yet have a real send or receive buffer. I'll be adding the buffers soon (unless someone beats me to it).
The problem is occurring with the POLLCT1 at the .read_bit label. I am calling ADDCT1 just before it, then entering into a tight polling loop. The loop does eventually exit, but only after the counter has wrapped around and hit the timer the second time. At least, I think that's what's happening. The value of rx_cnt+full_bit_time (with value of 434) should be greater than (later than) the current counter.
I'm hoping I just have a bug in my code that I can't see.
With that fixed, I have now updated the OP with another attachment: fds_demo2. This one entirely uses interrupts for TX, while still using a combination of interrupt and timer polling for RX. This version has been successfully tested to 1Mbps (with PST, that is)!
And that's just the TX code. I note you've very effectively used another timer to regulate the RX code so that cycles stolen by the TX code don't throw out the RX timing. Using the more precise mechanism for the TX means the electrical timing very stable.
That's some nice code ... making excellent use of some nice flexible hardware I suppose ... And so readable to boot.
Just updated the OP with a third version that is entirely interrupt driven. It doesn't run any faster, though. On the up side, this should be more power-friendly, as all of the polling has been removed.
Even more importantly, its damned fun to write!
In both cases, I think the safe baud rate is limited by the TX_start ISR. Because of the RDLONG, you must assume that TX_start will always take at least 34-36 clock cycles. And since this ISR can execute in the middle of the RX bit sampling (either the polled version in V2 or the interrupt version in V3), the RX sample could occur at least 36 clocks later than expected. Since the RX attempts to sample at the middle of the bit period, this means that the bit period must be at least 72 clock cycles wide (plus some for margin of error). On the current 50MHz clock, this means a maximum safe baud rate of ~625Kbps (assuming 80 clock cycles per bit period).
The only solution I've been able to think of so far is to actually sample RX much closer to the beginning of each bit time. As long as the bit transitions are fast, you could reasonably sample only a few clock cycles into each bit time. This would make the maximum baud rate dependent on ~(sample_delay+36) instead of ~(2*36). With clean signalling, you should be able to get back up to the ~1Mbps rate with the current 50MHz clock.
Do not forget sampling RX bits via interrupt on bit time periods may cause problems if any of the instructions mentioned in this post to you are executing at the time of interrupt.
forums.parallax.com/discussion/comment/1351880/#Comment_1351880
Interesting idea! At the very least, using WRFAST instead of WRLONG would be a good idea. I hadn't considered that the WRLONG could cause a TX timer event to get delayed for the same reason described above. Because the RX and TX are asynchronous, I doubt there's a reliable way to safely switch back and forth between RDFAST and WRFAST.
True. There are a few AUGS that I could get rid of.
BTW: What indicates an AUGS is used?
I was thinking with respect to receiving the RX bits and the code currently executing at the time. RX start bit could be delayed by say, a long REP sequence in non-isr code. If the non-isr code has further long REP sequences the counter interrupt for RX mid-bit sampling would be delayed, possibly misssing it. May need to stay with the isr for the loop, or modify the return address in IRETn register, saving interuupted program's return address in normal long, so it return to your own code, possibly dummy, whilst RX bits are incoming, then when all received, return to the original non-isr code. This method would allow other interrupts to occur, not just higher priority ones.
Anywhere I am using "##". In all of those cases, I can replace them with a register value instead of an immediate value.
Just updated my comment so it does not appear as a quote!
Maybe. But don't encourage Chip down this route, at least not yet! I want those smart pins finished! That might give us some other avenues for performance improvement.
Smartpins is hardware for hardware. That's cool and all but is not the same thing.
This is relevant to the discussion so far, but not just about fds-demo.
What if you synchronise with hub-access slot, and set a counter to interrupt at the point when hub data can be read / written. That would mean buffering the data internal to the cog/lut. The interrupt occurs, you have deducted an amout of time to enable reading of cog data to pass to the hub. Then at the moment the hub slot is available the next instruction is / read / write. If read, save data in cog/lut for TX routine. IRETn.
It would be overhead to some degree, but it would also mean not waiting for the hub as such. Your pure cog code can run merrily along until it runs out of or has generated enough data, and then has to wait.
Actually, if there was Special Function Register which you could do a RDBYTE/WORD/LONG or WRBYTE/WORD/LONG, which stored the data, and some bits hidden stored the registers, including ptr involved, then when hub access came round it was *automatically* executed, you can have a higher throughput of cog code without waiting on the hub so much, by sometimes reading data in advance. If the WAITxxxx POLLxxx wee extended to provide status of this it would be a useful transfer mechanism in the background.
And even synchronised still incurs a minimum number of clocks that will probably be 6 or so for a RDLONG.
Indeed, then I thought ofthe backgroun read/write via SFR.