6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 8:48 am

All times are UTC




Post new topic Reply to topic  [ 21 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Wed Jan 31, 2024 6:32 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
gilhad wrote:
Maybe it may help (or not) - I plan to check if "The End is near" (queue nearly full) and set flow-controll to block more bytes comming (RTS), then when the "The End is here" full queue I disable generation of interrupts, so the last byte is still in ACIA and I get no indication about it, but is not discarted.

If you examine the  “background” listing file, you will see at line 03921 the code that tests the fill level of the RxQ and decides if RTS should be de-asserted.  The “high-water” level at which this occurs is 80 percent of queue capacity.

Quote:
Then the reading routine can enable interrupt again after reading a byte and (if the queue is reasonably empty) deassert the RTS too. (And I will get interrupt and read the last byte. At least I hope so.)

I generally do not mess with chip setups after the system has booted, primarily because it is too easy to introduce a sneaky bug if a register is changed at the wrong time, something that NXP mentions in the UART’s data sheet.  I especially would never disable a hardware interrupt that is critical to processing a data stream.  The UART runs asynchronously to the MPU, and it is theoretically possible to clear the receiver interrupt bit when the internal actions of the UART have already created an interrupt.  Depending on how the UART driver is written, a spurious IRQ might occur and cause deadlock.  Been there, done that.

The interrupt mask register in NXP’s 26- and 28-series UARTs is write-only.  If an interrupt is to be disabled or enabled on the fly, the interrupt mask register has to be shadowed in RAM, the shadow copy has to be manipulated with AND and ORA, or with TRB and TSB, and then written into the interrupt mask register to change the UART’s configuration.  That costs processing time in the most time-critical part of the firmware: the interrupt handler.

Anther thing to consider is my POC V1.3 unit runs at 16 MHz and has to wait-state I/O accesses.  Manipulating the receiver interrupt bit(s) means yet another wait-stated access to the UART, again in the most time-critical part of the firmware.

Quote:
If the flow-controll do not work, than I take as much of bytes as I can and start listening as soon as I can take any byte more.

That I don’t do.  I don’t want repeated overrun errors scrambling the data stream.  Any time the receiver interrupts, I read it, and if I can’t write the datum into the queue, it gets discarded.  If the remote station is responding to RTS, it has more than enough time to stop transmitting when RTS is de-asserted.  No matter how you handle it, a serial link to a device that doesn’t implement flow control is going to result in data stream corruption if your interrupt and foreground routines are not fast enough to read and process the incoming flow at a rate higher that can be sent by the remote station.

Quote:
As setting/clearing RTS is just one store instruction for me, I do not test, if RTS is set or not, and set it anyway, because it is less cyckles, then to read, compare and branch.

See above about wait-states that are incurred on any I/O access.  Each access to the UART will result in the clock being stretched an extra full cycle.  On the other hand,  a test of the bitfield that tracks which receivers have their RTS de-asserted is a direct page access that takes three cycles.  An access to the UART takes four cycles plus a wait state, which is effectively seven cycles.

Also, consider that I’m working with four UART channels, not one...

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 31, 2024 7:42 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
A tunable - and well-tuned - high water mark is a useful and important idea. The "right" level for the high water mark is a function of the system as a whole: the sender's behaviour, and the receiver's behaviour, and the line speed. What matters is not so much how many bytes are in the buffer, but how much space there is left: the receiver needs to react to that, signal the sender, and the sender needs to react to the signal. We see quite a lot of variation in that last part.


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 31, 2024 4:16 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
BigEd wrote:
A tunable - and well-tuned - high water mark is a useful and important idea.

Yep!  It’s especially critical if the serial link is running very fast, since IRQs will be closely spaced.  All serial ports on my POC unit are set to run at 115.2 KbpS in both directions.  Receiver FIFO sizes are are set to 16, the maximum, but the FIFO-full interrupt threshold is set to 12, which allows some time for interrupt latency.  I’ve got auto-RTS enabled as a last resort to avoid an overrun error.

Quote:
The "right" level for the high water mark is a function of the system as a whole: the sender’s behaviour, and the receiver’s behaviour, and the line speed.  What matters is not so much how many bytes are in the buffer, but how much space there is left: the receiver needs to react to that, signal the sender, and the sender needs to react to the signal.  We see quite a lot of variation in that last part.

I’ve noted that a couple of modems I’ve tested with POC V 1.3 tend to be on the slow side in responding to RTS.  In particular, a US Robotics Sportster (external with DCE TIA-232 interface) takes, on average, four datum times at 115,200 to cease transmission after RTS is de-asserted.  A serial printer I have hooked up to SIO channel D on POC V1 is also a bit of a laggard in that regard, although not as slow as the modem.

My basic configuration sets the high-water mark to 80 percent of queue size, which accommodates the external device’s response time to RTS being de-asserted, as well as the time required for the UART channel to respond to a de-assert RTS command, which I’ve determined from logic analyzer runs to be three X1 clock cycles, or ~0.81 microseconds.  These things together should reduce the likelihood of an overrun error.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Thu Feb 01, 2024 4:59 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 01, 2024 6:57 am 
Offline
User avatar

Joined: Fri Jan 26, 2024 5:47 am
Posts: 37
Location: Prague; Czech Republic; Europe; Earth
BigDumbDinosaur wrote:
gilhad wrote:
Maybe it may help (or not) - I plan to check if "The End is near" (queue nearly full) and set flow-controll to block more bytes comming (RTS), then when the "The End is here" full queue I disable generation of interrupts, so the last byte is still in ACIA and I get no indication about it, but is not discarted.

If you examine the  “background” listing file, you will see at line 03921 the code that tests the fill level of the RxQ and decides if RTS should be de-asserted.  The “high-water” level at which this occurs is 80 percent of queue capacity.

I am sorry, I did not read those files before I wrote my message :( You are absolutely right. I am starting to look into this whole field and many things obvious for you are new for me and I am rediscovering the wheel all the time.
BigDumbDinosaur wrote:
Quote:
Then the reading routine can enable interrupt again after reading a byte and (if the queue is reasonably empty) deassert the RTS too. (And I will get interrupt and read the last byte. At least I hope so.)

I generally do not mess with chip setups after the system has booted, primarily because it is too easy to introduce a sneaky bug if a register is changed at the wrong time, something that NXP mentions in the UART’s data sheet.  I especially would never disable a hardware interrupt that is critical to processing a data stream.  The UART runs asynchronously to the MPU, and it is theoretically possible to clear the receiver interrupt bit when the internal actions of the UART have already created an interrupt.  Depending on how the UART driver is written, a spurious IRQ might occur and cause deadlock.  Been there, done that.

Uh, I did not look deep enought, so I was not aware about this :(
BigDumbDinosaur wrote:
The interrupt mask register in NXP’s 26- and 28-series UARTs is write-only.  If an interrupt is to be disabled or enabled on the fly, the interrupt mask register has to be shadowed in RAM, the shadow copy has to be manipulated with AND and ORA, or with TRB and TSB, and then written into the interrupt mask register to change the UART’s configuration.  That costs processing time in the most time-critical part of the firmware: the interrupt handler.

Another thing to consider is my POC V1.3 unit runs at 16 MHz and has to wait-state I/O accesses.  Manipulating the receiver interrupt bit(s) means yet another wait-stated access to the UART, again in the most time-critical part of the firmware.

My actual system was build without possibility of wait states and runs at nearly 2MHz, so I was not aware about this too (and it would bite me later).
BigDumbDinosaur wrote:
Quote:
If the flow-controll do not work, than I take as much of bytes as I can and start listening as soon as I can take any byte more.

That I don’t do.  I don’t want repeated overrun errors scrambling the data stream.  Any time the receiver interrupts, I read it, and if I can’t write the datum into the queue, it gets discarded.  If the remote station is responding to RTS, it has more than enough time to stop transmitting when RTS is de-asserted.  No matter how you handle it, a serial link to a device that doesn’t implement flow control is going to result in data stream corruption if your interrupt and foreground routines are not fast enough to read and process the incoming flow at a rate higher that can be sent by the remote station.

Quote:
As setting/clearing RTS is just one store instruction for me, I do not test, if RTS is set or not, and set it anyway, because it is less cyckles, then to read, compare and branch.

See above about wait-states that are incurred on any I/O access.  Each access to the UART will result in the clock being stretched an extra full cycle.  On the other hand,  a test of the bitfield that tracks which receivers have their RTS de-asserted is a direct page access that takes three cycles.  An access to the UART takes four cycles plus a wait state, which is effectively seven cycles.

Also, consider that I’m working with four UART channels, not one...


My ACIA is MC68B50 and the RTS probabely does not work there the way I would like (at least I think so - I thinks, that asserting it would stop also my ability to transmit ("transmiting interrupt disabled" as side-effect)) so I was going to build simple flip-flop device to support my own output pin and break out some pins from my USB-to-UART convertor (PL2303) to support the RTS/CTS control also on is side.

I have HD63C09 processor running at nearly 2MHz and with 115.200 bps my interrupt handler is few cycles slow to fully manage it, so I am going to increase its speed up to 4MHz (at least it was able run so fast on NOP generator already) (or at 3MHz, if 4MHz would prove too fast and unreliable) and implement flow-control myself and improve the interrupt handler too (allocate fixed 256 bytes queues so overflow is managed by HW nature of 8bit registers, and optimize out some instructions too). Your POC V1.3 is stil way faster ...

As I got from the newer parts of this topic, you have already better schema implemented, so while my message did not help you, your responce helped me a lot :D

_________________
http://micro-corner.gilhad.cz/, http://8bit.gilhad.cz/6809/Expanduino/Expanduino_I.html, http://comp24.gilhad.cz/Comp24-specification.html, and many others


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 01, 2024 1:38 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 247
This is an interesting method to keep the high bit either set or clear with wraparound in the lower 7 bits (if I understand it correctly).
Code:
        lda siogetrx,x        ;current RxQ _get_ pointer
        asl                   ;move bit 7 to carry
        inc                   ;double increment to...
        inc                   ;compensate for next step
        ror                   ;_get_+1
        sta siogetrx,x        ;update _get_ pointer
I think that's more elegant (hiding bit 7 in the carry, using only instructions that don't touch carry to perform the increment, and then bringing it back to bit 7 later) than the method I was thinking of, but it requires keeping track of which instructions affect which flags. I program on so many different architectures that I'm more likely to ignore which instructions do/don't affect flags and just force the bits to be the way I want them. You get bonus points.


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 01, 2024 5:31 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
SamCoVT wrote:
This is an interesting method to keep the high bit either set or clear with wraparound in the lower 7 bits (if I understand it correctly).
Code:
        lda siogetrx,x        ;current RxQ _get_ pointer
        asl                   ;move bit 7 to carry
        inc                   ;double increment to...
        inc                   ;compensate for next step
        ror                   ;_get_+1
        sta siogetrx,x        ;update _get_ pointer
I think that's more elegant (hiding bit 7 in the carry, using only instructions that don't touch carry to perform the increment, and then bringing it back to bit 7 later) than the method I was thinking of, but it requires keeping track of which instructions affect which flags. I program on so many different architectures that I'm more likely to ignore which instructions do/don't affect flags and just force the bits to be the way I want them. You get bonus points.

The bonus points should be awarded to gfoot:D

That sequence accommodates a 128 byte queue.  By adding one more ASL, one more ROR and two more INCs, it can be made to work with a 64 byte queue, which is about as small as I’d go.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: No registered users and 18 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: