diff mbox

[U-Boot,V2] console: Implement pre-console buffer

Message ID 1314708581-13678-1-git-send-email-graeme.russ@gmail.com
State Superseded
Headers show

Commit Message

Graeme Russ Aug. 30, 2011, 12:49 p.m. UTC
Allow redirection of console output prior to console initialisation to a
temporary buffer.

To enable this functionality, the board configuration file must define:
 - CONFIG_PRE_CONSOLE_BUFFER - Enable pre-console buffer
 - CONFIG_PRE_CON_BUF_ADDR - Base address of pre-console buffer
 - CONFIG_PRE_CON_BUF_SZ - Size of pre-console buffer (in bytes)

The pre-console buffer will buffer the last CONFIG_PRE_CON_BUF_SZ bytes
Any earlier characters are silently dropped.

Signed-off-by: Graeme Russ <graeme.russ@gmail.com>
---
Changes Since V1
 - Implemented circular buffer
 - Trivial code style corrections

 arch/arm/include/asm/global_data.h        |    3 ++
 arch/avr32/include/asm/global_data.h      |    3 ++
 arch/blackfin/include/asm/global_data.h   |    3 ++
 arch/m68k/include/asm/global_data.h       |    3 ++
 arch/microblaze/include/asm/global_data.h |    3 ++
 arch/mips/include/asm/global_data.h       |    3 ++
 arch/nios2/include/asm/global_data.h      |    3 ++
 arch/powerpc/include/asm/global_data.h    |    3 ++
 arch/sh/include/asm/global_data.h         |    3 ++
 arch/sparc/include/asm/global_data.h      |    3 ++
 arch/x86/include/asm/global_data.h        |    3 ++
 common/console.c                          |   49 +++++++++++++++++++++++++++-
 12 files changed, 80 insertions(+), 2 deletions(-)

--
1.7.5.2.317.g391b14

Comments

Mike Frysinger Aug. 30, 2011, 5:19 p.m. UTC | #1
On Tuesday, August 30, 2011 08:49:41 Graeme Russ wrote:
> -	if (!gd->have_console)
> +	if (!gd->have_console) {
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +		pre_console_putc(c);
> +#endif
>  		return;
> +	}

add inline stubs for the pre_console_xxx helpers and you can drop all the 
ifdef's through out the rest of the code

#ifdef CONFIG_PRE_CONSOLE_BUFFER
... normal defines for these ...
#else
static inline void pre_console_putc(const char c) {}
static inline void pre_console_puts(const char *s) {}
#endif
-mike
Simon Glass Aug. 30, 2011, 7:45 p.m. UTC | #2
Hi Graeme,

On Tue, Aug 30, 2011 at 5:49 AM, Graeme Russ <graeme.russ@gmail.com> wrote:
> Allow redirection of console output prior to console initialisation to a
> temporary buffer.
>
> To enable this functionality, the board configuration file must define:
>  - CONFIG_PRE_CONSOLE_BUFFER - Enable pre-console buffer
>  - CONFIG_PRE_CON_BUF_ADDR - Base address of pre-console buffer
>  - CONFIG_PRE_CON_BUF_SZ - Size of pre-console buffer (in bytes)

Please can you add these to the README?

Also I wonder if it is safe to have boards setting the buffer address.
Should not arch/xxx/lib/board.c work this out based on
CONFIG_SYS_INIT_SP_ADDR as it does all the other memory areas?

>
> The pre-console buffer will buffer the last CONFIG_PRE_CON_BUF_SZ bytes
> Any earlier characters are silently dropped.
>
> Signed-off-by: Graeme Russ <graeme.russ@gmail.com>
> ---
> Changes Since V1
>  - Implemented circular buffer
>  - Trivial code style corrections
>
>  arch/arm/include/asm/global_data.h        |    3 ++
>  arch/avr32/include/asm/global_data.h      |    3 ++
>  arch/blackfin/include/asm/global_data.h   |    3 ++
>  arch/m68k/include/asm/global_data.h       |    3 ++
>  arch/microblaze/include/asm/global_data.h |    3 ++
>  arch/mips/include/asm/global_data.h       |    3 ++
>  arch/nios2/include/asm/global_data.h      |    3 ++
>  arch/powerpc/include/asm/global_data.h    |    3 ++
>  arch/sh/include/asm/global_data.h         |    3 ++
>  arch/sparc/include/asm/global_data.h      |    3 ++
>  arch/x86/include/asm/global_data.h        |    3 ++
>  common/console.c                          |   49 +++++++++++++++++++++++++++-
>  12 files changed, 80 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h
> index 4fc51fd..b85b7fe 100644
> --- a/arch/arm/include/asm/global_data.h
> +++ b/arch/arm/include/asm/global_data.h
> @@ -38,6 +38,9 @@ typedef       struct  global_data {
>        unsigned long   flags;
>        unsigned long   baudrate;
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        unsigned long   env_addr;       /* Address  of Environment struct */
>        unsigned long   env_valid;      /* Checksum of Environment valid? */
>        unsigned long   fb_base;        /* base address of frame buffer */
> diff --git a/arch/avr32/include/asm/global_data.h b/arch/avr32/include/asm/global_data.h
> index 4ef8fc5..5c654bd 100644
> --- a/arch/avr32/include/asm/global_data.h
> +++ b/arch/avr32/include/asm/global_data.h
> @@ -38,6 +38,9 @@ typedef       struct  global_data {
>        unsigned long   baudrate;
>        unsigned long   stack_end;      /* highest stack address */
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        unsigned long   reloc_off;      /* Relocation Offset */
>        unsigned long   env_addr;       /* Address of env struct */
>        unsigned long   env_valid;      /* Checksum of env valid? */
> diff --git a/arch/blackfin/include/asm/global_data.h b/arch/blackfin/include/asm/global_data.h
> index eba5e93..f7aa711 100644
> --- a/arch/blackfin/include/asm/global_data.h
> +++ b/arch/blackfin/include/asm/global_data.h
> @@ -45,6 +45,9 @@ typedef struct global_data {
>        unsigned long board_type;
>        unsigned long baudrate;
>        unsigned long have_console;     /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        phys_size_t ram_size;           /* RAM size */
>        unsigned long env_addr; /* Address  of Environment struct */
>        unsigned long env_valid;        /* Checksum of Environment valid? */
> diff --git a/arch/m68k/include/asm/global_data.h b/arch/m68k/include/asm/global_data.h
> index fc486fd..0ba2b43 100644
> --- a/arch/m68k/include/asm/global_data.h
> +++ b/arch/m68k/include/asm/global_data.h
> @@ -57,6 +57,9 @@ typedef       struct  global_data {
>        unsigned long   env_addr;       /* Address  of Environment struct       */
>        unsigned long   env_valid;      /* Checksum of Environment valid?       */
>        unsigned long   have_console;   /* serial_init() was called             */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>  #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
>        unsigned long   fb_base;        /* Base addr of framebuffer memory */
>  #endif
> diff --git a/arch/microblaze/include/asm/global_data.h b/arch/microblaze/include/asm/global_data.h
> index 557ad27..6e8537c 100644
> --- a/arch/microblaze/include/asm/global_data.h
> +++ b/arch/microblaze/include/asm/global_data.h
> @@ -39,6 +39,9 @@ typedef       struct  global_data {
>        unsigned long   flags;
>        unsigned long   baudrate;
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        unsigned long   env_addr;       /* Address  of Environment struct */
>        unsigned long   env_valid;      /* Checksum of Environment valid? */
>        unsigned long   fb_base;        /* base address of frame buffer */
> diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h
> index 271a290..b193517 100644
> --- a/arch/mips/include/asm/global_data.h
> +++ b/arch/mips/include/asm/global_data.h
> @@ -41,6 +41,9 @@ typedef       struct  global_data {
>        unsigned long   flags;
>        unsigned long   baudrate;
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        phys_size_t     ram_size;       /* RAM size */
>        unsigned long   reloc_off;      /* Relocation Offset */
>        unsigned long   env_addr;       /* Address  of Environment struct */
> diff --git a/arch/nios2/include/asm/global_data.h b/arch/nios2/include/asm/global_data.h
> index 2c4a719..d9f0664 100644
> --- a/arch/nios2/include/asm/global_data.h
> +++ b/arch/nios2/include/asm/global_data.h
> @@ -29,6 +29,9 @@ typedef       struct  global_data {
>        unsigned long   baudrate;
>        unsigned long   cpu_clk;        /* CPU clock in Hz!             */
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        phys_size_t     ram_size;       /* RAM size */
>        unsigned long   env_addr;       /* Address  of Environment struct */
>        unsigned long   env_valid;      /* Checksum of Environment valid */
> diff --git a/arch/powerpc/include/asm/global_data.h b/arch/powerpc/include/asm/global_data.h
> index a33ca2f..7fcaf38 100644
> --- a/arch/powerpc/include/asm/global_data.h
> +++ b/arch/powerpc/include/asm/global_data.h
> @@ -138,6 +138,9 @@ typedef     struct  global_data {
>        unsigned long   env_addr;       /* Address  of Environment struct       */
>        unsigned long   env_valid;      /* Checksum of Environment valid?       */
>        unsigned long   have_console;   /* serial_init() was called             */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>  #if defined(CONFIG_SYS_ALLOC_DPRAM) || defined(CONFIG_CPM2)
>        unsigned int    dp_alloc_base;
>        unsigned int    dp_alloc_top;
> diff --git a/arch/sh/include/asm/global_data.h b/arch/sh/include/asm/global_data.h
> index 0c09ba9..1b782fc 100644
> --- a/arch/sh/include/asm/global_data.h
> +++ b/arch/sh/include/asm/global_data.h
> @@ -34,6 +34,9 @@ typedef       struct global_data
>        unsigned long   baudrate;
>        unsigned long   cpu_clk;        /* CPU clock in Hz! */
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        phys_size_t     ram_size;       /* RAM size */
>        unsigned long   env_addr;       /* Address  of Environment struct */
>        unsigned long   env_valid;      /* Checksum of Environment valid */
> diff --git a/arch/sparc/include/asm/global_data.h b/arch/sparc/include/asm/global_data.h
> index 9b14674..a1e4b44 100644
> --- a/arch/sparc/include/asm/global_data.h
> +++ b/arch/sparc/include/asm/global_data.h
> @@ -53,6 +53,9 @@ typedef struct global_data {
>        unsigned long env_valid;        /* Checksum of Environment valid?       */
>        unsigned long have_console;     /* serial_init() was called */
>
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>  #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
>        unsigned long fb_base;  /* Base address of framebuffer memory   */
>  #endif
> diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
> index f977dbe..6cf7955 100644
> --- a/arch/x86/include/asm/global_data.h
> +++ b/arch/x86/include/asm/global_data.h
> @@ -40,6 +40,9 @@ typedef       struct global_data {
>        unsigned long   flags;
>        unsigned long   baudrate;
>        unsigned long   have_console;   /* serial_init() was called */
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       unsigned long   precon_buf_idx; /* Pre-Console buffer index */
> +#endif
>        unsigned long   reloc_off;      /* Relocation Offset */
>        unsigned long   load_off;       /* Load Offset */
>        unsigned long   env_addr;       /* Address  of Environment struct */
> diff --git a/common/console.c b/common/console.c
> index b23d933..ae87148 100644
> --- a/common/console.c
> +++ b/common/console.c
> @@ -329,6 +329,35 @@ int tstc(void)
>        return serial_tstc();
>  }
>
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +#define CIRC_BUF_IDX(idx) ((idx) % CONFIG_PRE_CON_BUF_SZ)

The division here sticks in the craw, but unless we go with
CONFIG_PRE_CON_BUF_SZ_LOG2 then I don't see an easy way around it, and
since this is serial output we can't honestly claim to worry much
about performance.

> +
> +void pre_console_putc(const char c)
> +{
> +       char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
> +
> +       buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c;
> +}
> +
> +void pre_console_puts(const char *s)
> +{
> +       while (*s)
> +               pre_console_putc(*s++);
> +}
> +
> +void print_pre_console_buffer(void)
> +{
> +       unsigned long i = 0;
> +       char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
> +
> +       if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ)
> +               i = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ;

Gosh this is clever - perhaps a little comment?

> +
> +       while (i < gd->precon_buf_idx)
> +               putc(buffer[CIRC_BUF_IDX(i++)]);
> +}
> +#endif
> +
>  void putc(const char c)
>  {
>  #ifdef CONFIG_SILENT_CONSOLE
> @@ -341,8 +370,12 @@ void putc(const char c)
>                return;
>  #endif
>
> -       if (!gd->have_console)
> +       if (!gd->have_console) {
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +               pre_console_putc(c);
> +#endif
>                return;
> +       }
>
>        if (gd->flags & GD_FLG_DEVINIT) {
>                /* Send to the standard output */
> @@ -365,8 +398,12 @@ void puts(const char *s)
>                return;
>  #endif
>
> -       if (!gd->have_console)
> +       if (!gd->have_console) {
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +               pre_console_puts(s);
> +#endif
>                return;
> +       }
>
>        if (gd->flags & GD_FLG_DEVINIT) {
>                /* Send to the standard output */
> @@ -383,8 +420,10 @@ int printf(const char *fmt, ...)
>        uint i;
>        char printbuffer[CONFIG_SYS_PBSIZE];
>
> +#ifndef CONFIG_PRE_CONSOLE_BUFFER
>        if (!gd->have_console)
>                return 0;
> +#endif
>
>        va_start(args, fmt);
>
> @@ -404,8 +443,10 @@ int vprintf(const char *fmt, va_list args)
>        uint i;
>        char printbuffer[CONFIG_SYS_PBSIZE];
>
> +#ifndef CONFIG_PRE_CONSOLE_BUFFER
>        if (!gd->have_console)
>                return 0;
> +#endif
>
>        /* For this to work, printbuffer must be larger than
>         * anything we ever want to print.
> @@ -547,6 +588,10 @@ int console_init_f(void)
>                gd->flags |= GD_FLG_SILENT;
>  #endif
>
> +#ifdef CONFIG_PRE_CONSOLE_BUFFER
> +       print_pre_console_buffer();
> +#endif
> +
>        return 0;
>  }
>
> --
> 1.7.5.2.317.g391b14
>
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
>
Wolfgang Denk Aug. 30, 2011, 7:52 p.m. UTC | #3
Dear Simon Glass,

In message <CAPnjgZ1fXCCFDcwCWgZD7H-siVEV-H+Ks5TPeiT=nGS743fzcg@mail.gmail.com> you wrote:
> 
...
> > +#define CIRC_BUF_IDX(idx) ((idx) % CONFIG_PRE_CON_BUF_SZ)
> 
> The division here sticks in the craw, but unless we go with

Does it?  Why?

> CONFIG_PRE_CON_BUF_SZ_LOG2 then I don't see an easy way around it, and
> since this is serial output we can't honestly claim to worry much
> about performance.

Please see my previous posting
(http://article.gmane.org/gmane.comp.boot-loaders.u-boot/106810)

I don't think 3 additional addembler instructions really play a big
role here.

Best regards,

Wolfgang Denk
Mike Frysinger Aug. 30, 2011, 7:58 p.m. UTC | #4
On Tuesday, August 30, 2011 15:52:38 Wolfgang Denk wrote:
> Simon Glass wrote:
> > > +#define CIRC_BUF_IDX(idx) ((idx) % CONFIG_PRE_CON_BUF_SZ)
> > 
> > The division here sticks in the craw, but unless we go with
> 
> Does it?  Why?
> 
> > CONFIG_PRE_CON_BUF_SZ_LOG2 then I don't see an easy way around it, and
> > since this is serial output we can't honestly claim to worry much
> > about performance.
> 
> Please see my previous posting
> (http://article.gmane.org/gmane.comp.boot-loaders.u-boot/106810)
> 
> I don't think 3 additional addembler instructions really play a big
> role here.

i'm pretty sure if you define CONFIG_PRE_CON_BUF_SZ as a power of 2 value, you 
get nice & simple assembly code.  so if the generated code is undesirable, 
pick a CONFIG value that is power-of-2 ?
-mike
Simon Glass Aug. 30, 2011, 8:07 p.m. UTC | #5
Hi Wolfgang,

On Tue, Aug 30, 2011 at 12:52 PM, Wolfgang Denk <wd@denx.de> wrote:
> Dear Simon Glass,
>
> In message <CAPnjgZ1fXCCFDcwCWgZD7H-siVEV-H+Ks5TPeiT=nGS743fzcg@mail.gmail.com> you wrote:
>>
> ...
>> > +#define CIRC_BUF_IDX(idx) ((idx) % CONFIG_PRE_CON_BUF_SZ)
>>
>> The division here sticks in the craw, but unless we go with
>
> Does it?  Why?

Only because of the division. But as Mike mentions later, the compiler
will avoid it for powers of two. So all is well.

>
>> CONFIG_PRE_CON_BUF_SZ_LOG2 then I don't see an easy way around it, and
>> since this is serial output we can't honestly claim to worry much
>> about performance.
>
> Please see my previous posting
> (http://article.gmane.org/gmane.comp.boot-loaders.u-boot/106810)
>
> I don't think 3 additional addembler instructions really play a big
> role here.

Yes I saw it, thanks.

Regards,
Simon

>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> Every time history repeats itself the price goes up.
>
Wolfgang Denk Aug. 30, 2011, 8:08 p.m. UTC | #6
Dear Mike Frysinger,

In message <201108301558.08010.vapier@gentoo.org> you wrote:
>
> > Please see my previous posting
> > (http://article.gmane.org/gmane.comp.boot-loaders.u-boot/106810)
> > 
> > I don't think 3 additional addembler instructions really play a big
> > role here.
>
> i'm pretty sure if you define CONFIG_PRE_CON_BUF_SZ as a power of 2 value, you 
> get nice & simple assembly code.  so if the generated code is undesirable,
> pick a CONFIG value that is power-of-2 ?

This was the test code I compiled:

---------------------------------------------
#define	CONFIG_SYS_TMP_CON_BUF_SZ	1024

int foo(int i)
{
	return i & (CONFIG_SYS_TMP_CON_BUF_SZ-1);
}

int bar(int i)
{
	return i % CONFIG_SYS_TMP_CON_BUF_SZ;
}
---------------------------------------------

So was actually checking for a power-of-2 value.

Best regards,

Wolfgang Denk
Simon Glass Aug. 30, 2011, 8:18 p.m. UTC | #7
Hi Wolfgang,

On Tue, Aug 30, 2011 at 1:08 PM, Wolfgang Denk <wd@denx.de> wrote:
> Dear Mike Frysinger,
>
> In message <201108301558.08010.vapier@gentoo.org> you wrote:
>>
>> > Please see my previous posting
>> > (http://article.gmane.org/gmane.comp.boot-loaders.u-boot/106810)
>> >
>> > I don't think 3 additional addembler instructions really play a big
>> > role here.
>>
>> i'm pretty sure if you define CONFIG_PRE_CON_BUF_SZ as a power of 2 value, you
>> get nice & simple assembly code.  so if the generated code is undesirable,
>> pick a CONFIG value that is power-of-2 ?
>
> This was the test code I compiled:
>
> ---------------------------------------------
> #define CONFIG_SYS_TMP_CON_BUF_SZ       1024
>
> int foo(int i)
> {
>        return i & (CONFIG_SYS_TMP_CON_BUF_SZ-1);
> }
>
> int bar(int i)
> {
>        return i % CONFIG_SYS_TMP_CON_BUF_SZ;
> }
> ---------------------------------------------
>
> So was actually checking for a power-of-2 value.

In case it is interesting (which may be unlikely) here is the code
generated by my compiler (common code stripped) for your example, and
one I added. It seems that % makes it worry about sign.

#define CONFIG_SYS_TMP_CON_BUF_SZ       1024

int foo(int i)
{
       return i & (CONFIG_SYS_TMP_CON_BUF_SZ-1);
  14:	e1a03b03 	lsl	r3, r3, #22
  18:	e1a03b23 	lsr	r3, r3, #22
}

int bar(int i)
{
       return i % CONFIG_SYS_TMP_CON_BUF_SZ;
  40:	e1a02fc3 	asr	r2, r3, #31
  44:	e1a02b22 	lsr	r2, r2, #22
  48:	e0833002 	add	r3, r3, r2
  4c:	e1a03b03 	lsl	r3, r3, #22
  50:	e1a03b23 	lsr	r3, r3, #22
  54:	e0623003 	rsb	r3, r2, r3
}

int lee(int i)
{
       return i % (CONFIG_SYS_TMP_CON_BUF_SZ - 1);
  7c:	e3003803 	movw	r3, #2051	; 0x803
  80:	e3483020 	movt	r3, #32800	; 0x8020
  84:	e0c31293 	smull	r1, r3, r3, r2
  88:	e0833002 	add	r3, r3, r2
  8c:	e1a014c3 	asr	r1, r3, #9
  90:	e1a03fc2 	asr	r3, r2, #31
  94:	e0633001 	rsb	r3, r3, r1
  98:	e1a01003 	mov	r1, r3
  9c:	e1a01501 	lsl	r1, r1, #10
  a0:	e0631001 	rsb	r1, r3, r1
  a4:	e0613002 	rsb	r3, r1, r2
}

Regards,
Simon

>
> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> "Maintain an awareness for contribution --  to  your  schedule,  your
> project, our company."                         - A Group of Employees
>
Wolfgang Denk Aug. 30, 2011, 8:57 p.m. UTC | #8
Dear Simon Glass,

In message <CAPnjgZ1dQXMVeFk47=Gg5y+TCHG7WTdGk0b1WjSqOBhu2d0xnQ@mail.gmail.com> you wrote:
> 
> In case it is interesting (which may be unlikely) here is the code
> generated by my compiler (common code stripped) for your example, and

Which architecture / which compiler is this?

> one I added. It seems that % makes it worry about sign.

Indeed!  We should use unsigned numbers here.  MUCH better now:


unsigned int ufoo(unsigned int i)
{
        return i & (CONFIG_SYS_TMP_CON_BUF_SZ-1);
}

For ARM, BUF_SZ = 1024

        mov     r0, r0, asl #22
        mov     r0, r0, lsr #22

For PPC, BUF_SZ = 1024

	rlwinm 3,3,0,22,31

The following two entries make no sense, just for completeness:

For ARM, BUF_SZ = 1021  (Note: 1021 is a prime number)

	and     r0, r0, #1020

For PPC, BUF_SZ = 1021

        rlwinm 3,3,0,22,29



unsigned int ubar(unsigned int i)
{
        return i % CONFIG_SYS_TMP_CON_BUF_SZ;
}

For ARM, BUF_SZ = 1024

        mov     r0, r0, asl #22
        mov     r0, r0, lsr #22

For PPC, BUF_SZ = 1024

	rlwinm 3,3,0,22,31

For ARM, BUF_SZ = 1021

        ldr     r2, .L11
        mov     ip, r0
        umull   r1, r3, r2, r0
        rsb     r1, r3, r0
        add     r3, r3, r1, lsr #1
        mov     r3, r3, lsr #9
        mov     r0, r3, asl #10
        sub     r0, r0, r3, asl #2
        add     r0, r0, r3
        rsb     r0, r0, ip

For PPC, BUF_SZ = 1021

        lis 0,0xc0
        ori 0,0,36973
        mulhwu 0,3,0
        subf 9,0,3
        srwi 9,9,1
        add 0,0,9
        srwi 0,0,9
        mulli 0,0,1021
        subf 3,0,3


So indeed we should use unsigned arithmetics, and try to use
power-of-two sizes where possible.

Best regards,

Wolfgang Denk
Simon Glass Aug. 30, 2011, 9:02 p.m. UTC | #9
Hi Wolfgang,

On Tue, Aug 30, 2011 at 1:57 PM, Wolfgang Denk <wd@denx.de> wrote:
> Dear Simon Glass,
>
> In message <CAPnjgZ1dQXMVeFk47=Gg5y+TCHG7WTdGk0b1WjSqOBhu2d0xnQ@mail.gmail.com> you wrote:
>>
>> In case it is interesting (which may be unlikely) here is the code
>> generated by my compiler (common code stripped) for your example, and
>
> Which architecture / which compiler is this?

It was ARM, gcc version 4.4.3.

>
>> one I added. It seems that % makes it worry about sign.
>
> Indeed!  We should use unsigned numbers here.  MUCH better now:
>

Yes it is much better. Perhaps an (unsigned) cast in the C code is
better than hoping that the board author uses 1024U or similar.

>
> unsigned int ufoo(unsigned int i)
> {
>        return i & (CONFIG_SYS_TMP_CON_BUF_SZ-1);
> }
>
> For ARM, BUF_SZ = 1024
>
>        mov     r0, r0, asl #22
>        mov     r0, r0, lsr #22
>
> For PPC, BUF_SZ = 1024
>
>        rlwinm 3,3,0,22,31
>
> The following two entries make no sense, just for completeness:
>
> For ARM, BUF_SZ = 1021  (Note: 1021 is a prime number)
>
>        and     r0, r0, #1020
>
> For PPC, BUF_SZ = 1021
>
>        rlwinm 3,3,0,22,29
>
>
>
> unsigned int ubar(unsigned int i)
> {
>        return i % CONFIG_SYS_TMP_CON_BUF_SZ;
> }
>
> For ARM, BUF_SZ = 1024
>
>        mov     r0, r0, asl #22
>        mov     r0, r0, lsr #22
>
> For PPC, BUF_SZ = 1024
>
>        rlwinm 3,3,0,22,31
>
> For ARM, BUF_SZ = 1021
>
>        ldr     r2, .L11
>        mov     ip, r0
>        umull   r1, r3, r2, r0
>        rsb     r1, r3, r0
>        add     r3, r3, r1, lsr #1
>        mov     r3, r3, lsr #9
>        mov     r0, r3, asl #10
>        sub     r0, r0, r3, asl #2
>        add     r0, r0, r3
>        rsb     r0, r0, ip
>
> For PPC, BUF_SZ = 1021
>
>        lis 0,0xc0
>        ori 0,0,36973
>        mulhwu 0,3,0
>        subf 9,0,3
>        srwi 9,9,1
>        add 0,0,9
>        srwi 0,0,9
>        mulli 0,0,1021
>        subf 3,0,3
>
>
> So indeed we should use unsigned arithmetics, and try to use
> power-of-two sizes where possible.
>

Yes indeed.

Regards,
Simon

> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> You can't evaluate a man by logic alone.
>        -- McCoy, "I, Mudd", stardate 4513.3
>
Graeme Russ Aug. 30, 2011, 11 p.m. UTC | #10
Hi Simon, Mike, Wolfgang,

On Wed, Aug 31, 2011 at 7:02 AM, Simon Glass <sjg@chromium.org> wrote:
> Hi Wolfgang,
>
> On Tue, Aug 30, 2011 at 1:57 PM, Wolfgang Denk <wd@denx.de> wrote:
>> Dear Simon Glass,
>>
>> In message <CAPnjgZ1dQXMVeFk47=Gg5y+TCHG7WTdGk0b1WjSqOBhu2d0xnQ@mail.gmail.com> you wrote:
>>>
>>> In case it is interesting (which may be unlikely) here is the code
>>> generated by my compiler (common code stripped) for your example, and
>>
>> Which architecture / which compiler is this?
>
> It was ARM, gcc version 4.4.3.
>
>>
>>> one I added. It seems that % makes it worry about sign.
>>
>> Indeed!  We should use unsigned numbers here.  MUCH better now:
>>
>
> Yes it is much better. Perhaps an (unsigned) cast in the C code is
> better than hoping that the board author uses 1024U or similar.
>

[snip]

>>
>>
>> So indeed we should use unsigned arithmetics, and try to use
>> power-of-two sizes where possible.

I'll do another spin (probably not tonight - maybe tomorrow) which covers:

 - inline stub functions to reduce #ifdef clutter
 - unsigned casting of buffer size
 - Documentation in README including the fact that setting the buffer size
   to a value which is a power of 2 will provide more optimised code (for
   reasonably sane compilers ;))

Now as for the calculation of the buffer address... It could be calculated
dynamically in board.c but this will require another ulong in gd and the
pre-console buffer will be unusable until the calculation is done (and any
inadvertant printf() before this will likely cause some very bad behaviour)
Also, x86 does not do these calculations dynamically and I also think it
might make the pre-buffer console avaialble in early init_f

Getting back to gd for a sec - This patch (like the flagify patch set I
posted before) impacts the structure of gd. Does this trigger in increment
to the API Version? And as observed before, if the structure of gd is
fixed for any given API version, then we have a problem with all the
#ifdef's that are already in gd. If it _is_ the case that the API version
expects a particular fixed structure for gd, then maybe we need to remove
all the #ifdefs from gd - Yes it will lead to a few dead ulongs for a few
boards, but I don't see any practical alternatives. I did think about
looking at doing some kind of CRC on the gd struct (like the generated
asm-offsets), but I could not think of a way to do it)

Regards,

Graeme
Graeme Russ Aug. 30, 2011, 11:39 p.m. UTC | #11
Hi Wolfgang

On Wed, Aug 31, 2011 at 9:00 AM, Graeme Russ <graeme.russ@gmail.com> wrote:
> Hi Simon, Mike, Wolfgang,
>
> On Wed, Aug 31, 2011 at 7:02 AM, Simon Glass <sjg@chromium.org> wrote:
>> Hi Wolfgang,
>>
>> On Tue, Aug 30, 2011 at 1:57 PM, Wolfgang Denk <wd@denx.de> wrote:
>>> Dear Simon Glass,
>>>
>>> In message <CAPnjgZ1dQXMVeFk47=Gg5y+TCHG7WTdGk0b1WjSqOBhu2d0xnQ@mail.gmail.com> you wrote:
>>>>

[snip]

>
> Getting back to gd for a sec - This patch (like the flagify patch set I
> posted before) impacts the structure of gd. Does this trigger in increment
> to the API Version? And as observed before, if the structure of gd is
> fixed for any given API version, then we have a problem with all the
> #ifdef's that are already in gd. If it _is_ the case that the API version
> expects a particular fixed structure for gd, then maybe we need to remove
> all the #ifdefs from gd - Yes it will lead to a few dead ulongs for a few
> boards, but I don't see any practical alternatives. I did think about
> looking at doing some kind of CRC on the gd struct (like the generated
> asm-offsets), but I could not think of a way to do it)

Another gd related thought - Is it safe to assume that gd will be cleared?
I hope so, as the pre-buffer console assumes gd->precon_buf_idx is
initially zero.

Regards,

Graeme
Mike Frysinger Aug. 31, 2011, 2:46 a.m. UTC | #12
On Tuesday, August 30, 2011 19:39:20 Graeme Russ wrote:
> Another gd related thought - Is it safe to assume that gd will be cleared?
> I hope so, as the pre-buffer console assumes gd->precon_buf_idx is
> initially zero.

i'm pretty sure you can.  the Blackfin arch certainly memset's it to 0.
-mike
diff mbox

Patch

diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h
index 4fc51fd..b85b7fe 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -38,6 +38,9 @@  typedef	struct	global_data {
 	unsigned long	flags;
 	unsigned long	baudrate;
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	unsigned long	env_addr;	/* Address  of Environment struct */
 	unsigned long	env_valid;	/* Checksum of Environment valid? */
 	unsigned long	fb_base;	/* base address of frame buffer */
diff --git a/arch/avr32/include/asm/global_data.h b/arch/avr32/include/asm/global_data.h
index 4ef8fc5..5c654bd 100644
--- a/arch/avr32/include/asm/global_data.h
+++ b/arch/avr32/include/asm/global_data.h
@@ -38,6 +38,9 @@  typedef	struct	global_data {
 	unsigned long	baudrate;
 	unsigned long	stack_end;	/* highest stack address */
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	unsigned long	reloc_off;	/* Relocation Offset */
 	unsigned long	env_addr;	/* Address of env struct */
 	unsigned long	env_valid;	/* Checksum of env valid? */
diff --git a/arch/blackfin/include/asm/global_data.h b/arch/blackfin/include/asm/global_data.h
index eba5e93..f7aa711 100644
--- a/arch/blackfin/include/asm/global_data.h
+++ b/arch/blackfin/include/asm/global_data.h
@@ -45,6 +45,9 @@  typedef struct global_data {
 	unsigned long board_type;
 	unsigned long baudrate;
 	unsigned long have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	phys_size_t ram_size;		/* RAM size */
 	unsigned long env_addr;	/* Address  of Environment struct */
 	unsigned long env_valid;	/* Checksum of Environment valid? */
diff --git a/arch/m68k/include/asm/global_data.h b/arch/m68k/include/asm/global_data.h
index fc486fd..0ba2b43 100644
--- a/arch/m68k/include/asm/global_data.h
+++ b/arch/m68k/include/asm/global_data.h
@@ -57,6 +57,9 @@  typedef	struct	global_data {
 	unsigned long	env_addr;	/* Address  of Environment struct	*/
 	unsigned long	env_valid;	/* Checksum of Environment valid?	*/
 	unsigned long	have_console;	/* serial_init() was called		*/
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
 	unsigned long	fb_base;	/* Base addr of framebuffer memory */
 #endif
diff --git a/arch/microblaze/include/asm/global_data.h b/arch/microblaze/include/asm/global_data.h
index 557ad27..6e8537c 100644
--- a/arch/microblaze/include/asm/global_data.h
+++ b/arch/microblaze/include/asm/global_data.h
@@ -39,6 +39,9 @@  typedef	struct	global_data {
 	unsigned long	flags;
 	unsigned long	baudrate;
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	unsigned long	env_addr;	/* Address  of Environment struct */
 	unsigned long	env_valid;	/* Checksum of Environment valid? */
 	unsigned long	fb_base;	/* base address of frame buffer */
diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h
index 271a290..b193517 100644
--- a/arch/mips/include/asm/global_data.h
+++ b/arch/mips/include/asm/global_data.h
@@ -41,6 +41,9 @@  typedef	struct	global_data {
 	unsigned long	flags;
 	unsigned long	baudrate;
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	phys_size_t	ram_size;	/* RAM size */
 	unsigned long	reloc_off;	/* Relocation Offset */
 	unsigned long	env_addr;	/* Address  of Environment struct */
diff --git a/arch/nios2/include/asm/global_data.h b/arch/nios2/include/asm/global_data.h
index 2c4a719..d9f0664 100644
--- a/arch/nios2/include/asm/global_data.h
+++ b/arch/nios2/include/asm/global_data.h
@@ -29,6 +29,9 @@  typedef	struct	global_data {
 	unsigned long	baudrate;
 	unsigned long	cpu_clk;	/* CPU clock in Hz!		*/
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	phys_size_t	ram_size;	/* RAM size */
 	unsigned long	env_addr;	/* Address  of Environment struct */
 	unsigned long	env_valid;	/* Checksum of Environment valid */
diff --git a/arch/powerpc/include/asm/global_data.h b/arch/powerpc/include/asm/global_data.h
index a33ca2f..7fcaf38 100644
--- a/arch/powerpc/include/asm/global_data.h
+++ b/arch/powerpc/include/asm/global_data.h
@@ -138,6 +138,9 @@  typedef	struct	global_data {
 	unsigned long	env_addr;	/* Address  of Environment struct	*/
 	unsigned long	env_valid;	/* Checksum of Environment valid?	*/
 	unsigned long	have_console;	/* serial_init() was called		*/
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 #if defined(CONFIG_SYS_ALLOC_DPRAM) || defined(CONFIG_CPM2)
 	unsigned int	dp_alloc_base;
 	unsigned int	dp_alloc_top;
diff --git a/arch/sh/include/asm/global_data.h b/arch/sh/include/asm/global_data.h
index 0c09ba9..1b782fc 100644
--- a/arch/sh/include/asm/global_data.h
+++ b/arch/sh/include/asm/global_data.h
@@ -34,6 +34,9 @@  typedef	struct global_data
 	unsigned long	baudrate;
 	unsigned long	cpu_clk;	/* CPU clock in Hz! */
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	phys_size_t	ram_size;	/* RAM size */
 	unsigned long	env_addr;	/* Address  of Environment struct */
 	unsigned long	env_valid;	/* Checksum of Environment valid */
diff --git a/arch/sparc/include/asm/global_data.h b/arch/sparc/include/asm/global_data.h
index 9b14674..a1e4b44 100644
--- a/arch/sparc/include/asm/global_data.h
+++ b/arch/sparc/include/asm/global_data.h
@@ -53,6 +53,9 @@  typedef struct global_data {
 	unsigned long env_valid;	/* Checksum of Environment valid?       */
 	unsigned long have_console;	/* serial_init() was called */

+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
 	unsigned long fb_base;	/* Base address of framebuffer memory   */
 #endif
diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
index f977dbe..6cf7955 100644
--- a/arch/x86/include/asm/global_data.h
+++ b/arch/x86/include/asm/global_data.h
@@ -40,6 +40,9 @@  typedef	struct global_data {
 	unsigned long	flags;
 	unsigned long	baudrate;
 	unsigned long	have_console;	/* serial_init() was called */
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	unsigned long	precon_buf_idx;	/* Pre-Console buffer index */
+#endif
 	unsigned long	reloc_off;	/* Relocation Offset */
 	unsigned long	load_off;	/* Load Offset */
 	unsigned long	env_addr;	/* Address  of Environment struct */
diff --git a/common/console.c b/common/console.c
index b23d933..ae87148 100644
--- a/common/console.c
+++ b/common/console.c
@@ -329,6 +329,35 @@  int tstc(void)
 	return serial_tstc();
 }

+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+#define CIRC_BUF_IDX(idx) ((idx) % CONFIG_PRE_CON_BUF_SZ)
+
+void pre_console_putc(const char c)
+{
+	char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
+
+	buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c;
+}
+
+void pre_console_puts(const char *s)
+{
+	while (*s)
+		pre_console_putc(*s++);
+}
+
+void print_pre_console_buffer(void)
+{
+	unsigned long i = 0;
+	char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;
+
+	if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ)
+		i = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ;
+
+	while (i < gd->precon_buf_idx)
+		putc(buffer[CIRC_BUF_IDX(i++)]);
+}
+#endif
+
 void putc(const char c)
 {
 #ifdef CONFIG_SILENT_CONSOLE
@@ -341,8 +370,12 @@  void putc(const char c)
 		return;
 #endif

-	if (!gd->have_console)
+	if (!gd->have_console) {
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+		pre_console_putc(c);
+#endif
 		return;
+	}

 	if (gd->flags & GD_FLG_DEVINIT) {
 		/* Send to the standard output */
@@ -365,8 +398,12 @@  void puts(const char *s)
 		return;
 #endif

-	if (!gd->have_console)
+	if (!gd->have_console) {
+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+		pre_console_puts(s);
+#endif
 		return;
+	}

 	if (gd->flags & GD_FLG_DEVINIT) {
 		/* Send to the standard output */
@@ -383,8 +420,10 @@  int printf(const char *fmt, ...)
 	uint i;
 	char printbuffer[CONFIG_SYS_PBSIZE];

+#ifndef CONFIG_PRE_CONSOLE_BUFFER
 	if (!gd->have_console)
 		return 0;
+#endif

 	va_start(args, fmt);

@@ -404,8 +443,10 @@  int vprintf(const char *fmt, va_list args)
 	uint i;
 	char printbuffer[CONFIG_SYS_PBSIZE];

+#ifndef CONFIG_PRE_CONSOLE_BUFFER
 	if (!gd->have_console)
 		return 0;
+#endif

 	/* For this to work, printbuffer must be larger than
 	 * anything we ever want to print.
@@ -547,6 +588,10 @@  int console_init_f(void)
 		gd->flags |= GD_FLG_SILENT;
 #endif

+#ifdef CONFIG_PRE_CONSOLE_BUFFER
+	print_pre_console_buffer();
+#endif
+
 	return 0;
 }