10 bits per byte (8 bits data, 1 start, 1 stop) Baud Bytes/s Sec/Byte uS/byte 2400 240 0.00420 4200 9600 960 0.001 1000 14400 1440 0.0007 700 28800 2880 0.000347 347 38400 3840 0.00026 260 57600 5760 0.000176 176 115200 11520 0.000087 87So with a port running at 56700, you have ~170 micro-seconds to recover each byte before it is overwritten by the following byte. This can be a problem because of the interrupt time of ethernet and other devices, which can be over 200us. Under NetBSD 1.0, the interrupt dispatch time for the com interrupt was ~25us on a 486/33.
A FIFO multiplies the time allowed before data is lost. A 16 byte FIFO at 56700 provides between 1400us (trigger at 8) and 2100us (trigger at 4). It can provide a maximum of 2600us (trigger at 1). The NetBSD com device triggers interrupts at 8 characters, therefore when using a 16550, if any device in the system takes longer than 1400us, characters can be lost causing a silo overflow.
The Hayes ESP (and other large FIFO) board is a sure fire cure for silo lossage. Its enormous buffers provide ~88,392us before overflow. The NetBSD, FreeBSD, and I assume Linux com drivers have been ported to support this device.
For those of you who are having com driver troubles on NetBSD/i386,
try
ftp://ftp.fwi.uva.nl/pub/comp/NetBSD/fvdl/com-290596.tar.gz.
Thanks to Frank for putting it up for ftp.
This is the faster serial driver as constructed by Onno van der Linden
(onno@simplex.nl). To use it, you need to:
- Have a NetBSD/i386 -current which includes the recent IPL_HIGH changes - Apply the icu.s.diff patch to /usr/src/sys/arch/i386/isa/icu.s - drop com.c into /usr/src/sys/dev/isa - recompile
>sys/kern/tty.c: change 3 clalloc()s in ttymalloc() to 4k. >sys/sys/tty.h: change TTYHOG to 4k.These alterations allow the tty subsystem to buffer more data than normal (1k).
Do the blocking and deprioritising of interrupt work on i386? Here are some old messages talking about this sort of thing:
From: "John F. Woods"Date: Thu, 06 Jul 1995 08:22:36 -0400 If there is a microsecond-resolution clock available on the x86, you can use the trick I used for interrupt latency calculations: modify the spl routines to figure out when and from where they were called, and keep track of who holds the interrupt mask. (This is going to be much trickier on the 386 than on the machine where I did this last, which had only one interrupt level.) If you have time, you can put macro wrappers around the spl routines to pass in identification strings (like __FILE__ and __LINE__, or even custom strings if you have lots of time), but it shouldn't be a trememdous amount of work (I hope) to have splxxx grovel up the stack to find a likely candidate for blame. This would give a very complete picture of the interrupt situation, though it might involve more time than you're able to put into it. (I might even try tackling this myself sometime, since it's a favorite hobby-horse of mine, but I'm not sure I have enough interesting hardware to really find unusual cases. I suppose that once I get it working, others could take the same code and run it on different hardware.)
From: Charles HannumDate: Thu, 6 Jul 1995 12:32:08 -0400 There are many things that can cause interrupts to be delayed for short periods. Combined with the overhead of actually entering the interrupt routine and calling up to the line discipline input routine, the total latency before the next character is read can easily be too high for an unbuffered serial port. There are a few ways to partially fix this: 1) Add an extra layer of buffering, to shorten the path inside the interrupt handler. This has been done. 2) Give tty interrupts a higher priority. I was planning to do this soon. You could go further and (almost) never allow the lower half of the interrupt handler to be blocked. This would give you close to the minimum possible latency. 3) Modify the line discipline interface to allow passing up a larger number of characters at once. For things like SLIP and PPP, this could significantly reduce overhead by eliminating function calls and allowing the line discipline to have tighter loops for several things. This would help prevent overflowing the secondary buffer. I would guess that items 2 and 3 could be implemented in a weekend. I don't currently have a weekend free, though. (hint, hint) The FreeBSD code mostly does item 2. It goes to a slight extreme on item 3, by inlining part of the generic tty interface into the serial driver. While this probably improves performance, it's not really acceptable from an architectural viewpoint.
From: Robert DobbsDate: Thu, 6 Jul 1995 15:05:25 -0700 Heres an outline of things that'd need to be done to support multiple character transfers. Note that nothing need be broken until the last step when the new functions are enabled. Everything else can be done while retaining the original interface. * new linesw function for multiple char transfer: l_mrint /sys/include/conf.h add l_mrint to linesw structure /sys/kern/tty_conf.c add NULL entries for l_mrint initialization * check the new l_mrint in com.c: if not NULL, use it rather than l_rint /sys/dev/isa/com.c /* other places? */ * add l_mrint interface stubs for slip and ppp /sys/net/if_sl.c slmultinput(int *p, int size, struct tty *tp) /sys/net/if_ppp.c pppmultinput(int *p, int size, struct tty *tp) * modify the slip and ppp stub functions to work ;) in if_sl.c, this would essentially entail wrapping the code from slinput() in a while loop. this would be a first step. i assume if_ppp.c would be the same way. * "turn on" the minput routines by setting the l_mrint pointer to the proper function.
From: Chris G DemetriouDate: Wed, 12 Jul 1995 05:01:37 -0400 > Heres an outline of things that'd need to be done to support multiple > character transfers. Note that nothing need be broken until the last > step when the new functions are enabled. Everything else can be done > while retaining the original interface. to my mind, if a "multiple input" routine is going to be created, it should completely replace the old "single input" routine. it's not very much extra work at all, to pass a pointer and a constant, than to pass a variable, and it relieves the need to maintain two both functions. that's a much more 'sweeping' change, though...