[U-Boot] RFC - How to speed up multiplexed input between serial andnetwork?

Bigler, Stefan Stefan.Bigler at keymile.com
Wed Oct 29 12:11:12 CET 2008


Hi

Here is patch for the driver for MPC8xx (SMC). If the idea is accepted
the then the driver for MPC82xx will be enhanced as well.

Best regards,
Stefan Bigler

diff --git a/cpu/mpc8xx/serial.c b/cpu/mpc8xx/serial.c
index ad02299..20440cd 100644
--- a/cpu/mpc8xx/serial.c
+++ b/cpu/mpc8xx/serial.c
@@ -108,17 +108,30 @@ static void smc_setbrg (void)
 	serial_setdivisor(cp);
 }
 
+
+typedef volatile struct SerialBuffer { 
+	cbd_t rxbd;	                                 /* Rx BD */
+	cbd_t txbd;			                         /* Tx
BD */
+#ifdef CONFIG_SMC_RXBUFLEN
+	uint  rxCharIndex;                           /* index for next
character to read */
+	volatile uchar rxbuf[CONFIG_SMC_RXBUFLEN];   /* rx buffers */
+#else
+	volatile uchar rxbuf[1];                     /* rx buffers */
+#endif
+	volatile uchar txbuf;                        /* tx buffers */
+} SerialBuffer;
+
 static int smc_init (void)
 {
 	volatile immap_t *im = (immap_t *)CFG_IMMR;
 	volatile smc_t *sp;
 	volatile smc_uart_t *up;
-	volatile cbd_t *tbdf, *rbdf;
 	volatile cpm8xx_t *cp = &(im->im_cpm);
 #if (!defined(CONFIG_8xx_CONS_SMC1)) && (defined(CONFIG_MPC823) ||
defined(CONFIG_MPC850))
 	volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport);
 #endif
 	uint	dpaddr;
+	SerialBuffer* rtx;
 
 	/* initialize pointers to SMC */
 
@@ -194,23 +207,26 @@ static int smc_init (void)
 	 */
 
 #ifdef CFG_ALLOC_DPRAM
-	dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ;
+	/* allocate 
+	 * the size of struct SerialBuffer with bd rx/tx, buffer rx/tx
and rx index  
+	 */
+	dpaddr = dpram_alloc_align ((sizeof(SerialBuffer)), 8);
 #else
 	dpaddr = CPM_SERIAL_BASE ;
 #endif
 
+	rtx = (SerialBuffer*)&cp->cp_dpmem[dpaddr];
 	/* Allocate space for two buffer descriptors in the DP ram.
 	 * For now, this address seems OK, but it may have to
 	 * change with newer versions of the firmware.
 	 * damm: allocating space after the two buffers for rx/tx data
 	 */
 
-	rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr];
-	rbdf->cbd_bufaddr = (uint) (rbdf+2);
-	rbdf->cbd_sc = 0;
-	tbdf = rbdf + 1;
-	tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1;
-	tbdf->cbd_sc = 0;
+	rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf;
+	rtx->rxbd.cbd_sc      = 0;
+
+	rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf;
+	rtx->txbd.cbd_sc      = 0;
 
 	/* Set up the uart parameters in the parameter ram.
 	*/
@@ -256,13 +272,21 @@ static int smc_init (void)
 
 	/* Make the first buffer the only buffer.
 	*/
-	tbdf->cbd_sc |= BD_SC_WRAP;
-	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
+	rtx->txbd.cbd_sc |= BD_SC_WRAP;
+	rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
 
+#ifdef CONFIG_SMC_RXBUFLEN
+	/* multi-character receive.
+	*/
+	up->smc_mrblr    = CONFIG_SMC_RXBUFLEN;
+	up->smc_maxidl   = 10;
+	rtx->rxCharIndex = 0;
+#else 
 	/* Single character receive.
 	*/
 	up->smc_mrblr = 1;
 	up->smc_maxidl = 0;
+#endif
 
 	/* Initialize Tx/Rx parameters.
 	*/
@@ -285,11 +309,16 @@ static int smc_init (void)
 static void
 smc_putc(const char c)
 {
-	volatile cbd_t		*tbdf;
-	volatile char		*buf;
 	volatile smc_uart_t	*up;
 	volatile immap_t	*im = (immap_t *)CFG_IMMR;
 	volatile cpm8xx_t	*cpmp = &(im->im_cpm);
+	SerialBuffer            *rtx;
+
+	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
+#ifdef CFG_SMC_UCODE_PATCH
+	up = (smc_uart_t *)&cpmp->cp_dpmem[up->smc_rpbase];
+#endif
+	rtx = (SerialBuffer *)&cpmp->cp_dpmem[up->smc_rbase];
 
 #ifdef CONFIG_MODEM_SUPPORT
 	if (gd->be_quiet)
@@ -299,24 +328,12 @@ smc_putc(const char c)
 	if (c == '\n')
 		smc_putc ('\r');
 
-	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
-#ifdef CFG_SMC_UCODE_PATCH
-	up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase];
-#endif
-
-	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
-
-	/* Wait for last character to go.
-	*/
-
-	buf = (char *)tbdf->cbd_bufaddr;
-
-	*buf = c;
-	tbdf->cbd_datlen = 1;
-	tbdf->cbd_sc |= BD_SC_READY;
+	rtx->txbuf = c;
+	rtx->txbd.cbd_datlen = 1;
+	rtx->txbd.cbd_sc |= BD_SC_READY;
 	__asm__("eieio");
 
-	while (tbdf->cbd_sc & BD_SC_READY) {
+	while (rtx->txbd.cbd_sc & BD_SC_READY) {
 		WATCHDOG_RESET ();
 		__asm__("eieio");
 	}
@@ -333,29 +350,39 @@ smc_puts (const char *s)
 static int
 smc_getc(void)
 {
-	volatile cbd_t		*rbdf;
-	volatile unsigned char	*buf;
 	volatile smc_uart_t	*up;
 	volatile immap_t	*im = (immap_t *)CFG_IMMR;
 	volatile cpm8xx_t	*cpmp = &(im->im_cpm);
-	unsigned char		c;
+	SerialBuffer            *rtx;
 
 	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
 #ifdef CFG_SMC_UCODE_PATCH
 	up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase];
 #endif
 
-	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+    rtx = (SerialBuffer *)&cpmp->cp_dpmem[up->smc_rbase];
+	unsigned char  c;
 
 	/* Wait for character to show up.
 	*/
-	buf = (unsigned char *)rbdf->cbd_bufaddr;
-
-	while (rbdf->cbd_sc & BD_SC_EMPTY)
+	while (rtx->rxbd.cbd_sc & BD_SC_EMPTY)
 		WATCHDOG_RESET ();
 
-	c = *buf;
-	rbdf->cbd_sc |= BD_SC_EMPTY;
+#ifdef CONFIG_SMC_RXBUFLEN
+	/* the characters are read one by one, use the rxCharIndex to
know the next char to deliver */
+	c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr+rtx->rxCharIndex);
+	rtx->rxCharIndex++;
+
+	/* check if all char are readout, then make prepare for next
receive */
+	if (rtx->rxCharIndex >= rtx->rxbd.cbd_datlen)
+	{
+		rtx->rxCharIndex  = 0;
+	rtx->rxbd.cbd_sc |= BD_SC_EMPTY;
+	}
+#else
+	c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr);
+	rtx->rxbd.cbd_sc |= BD_SC_EMPTY;
+#endif
 
 	return(c);
 }
@@ -363,19 +390,19 @@ smc_getc(void)
 static int
 smc_tstc(void)
 {
-	volatile cbd_t		*rbdf;
 	volatile smc_uart_t	*up;
 	volatile immap_t	*im = (immap_t *)CFG_IMMR;
 	volatile cpm8xx_t	*cpmp = &(im->im_cpm);
+	SerialBuffer            *rtx;
 
 	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
 #ifdef CFG_SMC_UCODE_PATCH
 	up = (smc_uart_t *) &cpmp->cp_dpmem[up->smc_rpbase];
 #endif
 
-	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+	rtx = (SerialBuffer *)&cpmp->cp_dpmem[up->smc_rbase];
 
-	return(!(rbdf->cbd_sc & BD_SC_EMPTY));
+	return(!(rtx->rxbd.cbd_sc & BD_SC_EMPTY));
 }
 
 struct serial_device serial_smc_device =


> -----Original Message-----
> From: u-boot-bounces at lists.denx.de
[mailto:u-boot-bounces at lists.denx.de]
> On Behalf Of Bigler, Stefan
> Sent: Wednesday, October 29, 2008 9:27 AM
> To: u-boot at lists.denx.de
> Subject: [U-Boot] RFC - How to speed up multiplexed input between
serial
> andnetwork?
> 
> Hi
> 
> We are trying to use U-Boot that it can be remote controlled over
> netconsole and in locally over the serial terminal.
> We were quite successful but we saw some latency issues on the serial
> terminal. The polling of the serial driver is too slow to get all
> characters. This does not allow you to e.g. to copy/paste, most of the
> characters are lost.
> 
> We analyzed the code and tried to speed it up, without the required
> improvement.
> The tests are done with an MPC852 at 66MHz and an MPC8247.
> 
> In the file common/console.c we added hooks to measure the time for
> tstc() execution.
> The measured time are:
> serial-driver     3 Microseconds
> nc		     15 Milliseconds
> 
> The result is, that the serial interface is polled only every 15
> Millisecond.
> On the serial interface with a line-rate of 115200 we receive aprox
> 10'000 Character every second. This is one character every 100
> Microsecond.
> 
> The serial driver has one buffer-descriptor with the space for one
> character. This results in a maximal polling period of 100
Microseconds.
> 
> The HW-FIFO for a MPC852T is 2 bytes.
> 
> 
> There are 2 possibilities to solve the problem:
> -----------------------------------------------
> a) make the netconsole faster
> b) make serial more "robust" and allow more latency
> 
> The better solution is of course to make the netconsole faster. But
can
> we reach 100 Microseconds?
> We can reduce it (as already done e.g. accelerate the readout of
> env-variables). To accelerate by factor 150 we need to do major
changes
> e.g. read-out the env if changed so we need a mechanism to see this.
> 
> On the other hand we can enhance the serial driver to "absorb" e.g.
one
> line that allows you to copy/paste.
> This is not a big code change but it needs more dp-ram.
> 
> The copy/paste test shows the following result
> copy          paste
> 0123456789 -> 0 -> first character
> 
> 
> a) So I tried to make the netconsole faster with the optimisation of
> tstc()
>
------------------------------------------------------------------------
> ---
> There is the possibility to do the getenv() only if the env is
changed.
> I added a "transactionId" what is incremented after every write to
env.
> So the user of env can check if the env changed and only read if
> changed.
> This reduced the tstc() of nc to 60 Microseconds. So the polling of
> serial is done every 70 Microseconds.
> In principle this should be fast enough to be able to copy paste
> copy          paste
> 0123456789 -> 013679 -> 50%
> 
> Why are we receiving only half of the character? This due to the fact
> that processing a character needs time. If we check how often we call
> getc() while copy/paste, this is every 180 Microsecond. The method
> getc() do not need lot of time, but the received character is sent
over
> nc before we get the next char. I think we cannot avoid this.
> 
> I do not see how we can reduce this time even further.
> 
> The measurement is also done without nc. There the getc() is called
> every 80-90 Microseconds. So we see that is little headroom to do
> additional processing!
> 
> 
> b) Make the serial driver more "robust" to absorb bursts
> --------------------------------------------------------
> I think it would make sense to be able to absorb the burst of one line
> e.g. 128 character.
> 
> This can be done in 2 way:
> b1) use more buffer descriptor with one character
> b2) use the feature of smc to allow multi-character buffer
> 
> b1) driver with multi buffer descriptor
> ---------------------------------------
> This is the possibility that is quite simple to implement, but needs
> more resources. I have already sent this. The required
dual-port-memory
> is high
> 128 bd * 8 byte plus 128 byte for character = 1152 byte more.
> (I also implemented this driver)
> 
> b2) driver with multi-character buffer
> --------------------------------------
> I have implemented this driver for MPC852T (SMC) and attached a patch.
> The additional use of DP-RAM is the size the buffer (e.g. 128 bytes)
and
> 4 bytes for an index to the next character to read.
> A define can be used to specify the size of the buffer. If undefined
the
> size is 1.
> 
> 
> Conclusion:
> -----------
> I do not see a good chance to be able to reduce the processing time in
> the netconsole below 100 Microseconds.
> 
> I expect copy/paste to work for a line (128 characters).
> 
> So I propose to enhance the serial driver.
> 
> Best regards,
> Stefan Bigler
> 
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
> 



More information about the U-Boot mailing list