Page 1 of 1

Some more Broadcom C Codes.

Posted: Thu Dec 19, 2013 10:25 pm
by lilzz

Code: Select all

// safe write to peripheral
void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
{
    if (debug)
    {
	printf("bcm2835_peri_write paddr %08X, value %08X\n", paddr, value);
    }
    else
    {
	// Make sure we dont rely on the first write, which may get
	// lost if the previous access was to a different peripheral.
	*paddr = value;
	*paddr = value;
    }
}
The above code doesn't make sense. Writing to the same location will give a safe write?
Different peripheral would have a different padder or memory mapped address. For a given a padder there's only a unique peripheral it's addressing.


// safe read from peripheral

Code: Select all

uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
{
    if (debug)
    {
	printf("bcm2835_peri_read  paddr %08X\n", paddr);
	return 0;
    }
    else
    {
	// Make sure we dont return the _last_ read which might get lost
	// if subsequent code changes to a differnt peripheral
	uint32_t ret = *paddr;
	uint32_t dummy = *paddr;
	return ret;
}
    }
Safe read, I don't get it either. first read gives to ret and return from ret. Doing a second read to dummy would help or affect that? how?

Re: Some more Broadcom C Codes.

Posted: Thu Dec 19, 2013 10:44 pm
by joan
Some times it's easiest just to accept statements at there face value.

Broadcom have some odd buses.
1.3 Peripheral access precautions for correct memory ordering

The BCM2835 system uses an AMBA AXI-compatible interface structure. In order to keep
the system complexity low and data throughput high, the BCM2835 AXI system does not
always return read data in-order2. The GPU has special logic to cope with data arriving out-
of-order; however the ARM core does not contain such logic. Therefore some precautions
must be taken when using the ARM to access peripherals.

Accesses to the same peripheral will always arrive and return in-order. It is only when
switching from one peripheral to another that data can arrive out-of-order. The simplest way
to make sure that data is processed in-order is to place a memory barrier instruction at critical
positions in the code. You should place:

A memory write barrier before the first write to a peripheral.
A memory read barrier after the last read of a peripheral.

It is not required to put a memory barrier instruction after each read or write access. Only at
those places in the code where it is possible that a peripheral read or write may be followed
by a read or write of a different peripheral. This is normally at the entry and exit points of the
peripheral service code.

As interrupts can appear anywhere in the code so you should safeguard those. If an interrupt
routine reads from a peripheral the routine should start with a memory read barrier. If an
interrupt routine writes to a peripheral the routine should end with a memory write barrier.

Normally a processor assumes that if it executes two read operations the data will arrive in order. So a read from location X followed by a read from location Y should return the data of location X first, followed by the data of location Y. Data arriving out of order can have disastrous consequences. For example:

a_status = *pointer_to_peripheral_a;
b_status = *pointer_to_peripheral_b;

Without precuations the values ending up in the variables a_status and b_status can be swapped around.

It is theoretical possible for writes to go ‘wrong’ but that is far more difficult to achieve. The AXI system
makes sure the data always arrives in-order at its intended destination. So:

*pointer_to_peripheral_a = value_a;
*pointer_to_peripheral_b = value_b;

will always give the expected result. The only time write data can arrive out-of-order is if two different
peripherals are connected to the same external equipment.

Re: Some more Broadcom C Codes.

Posted: Fri Dec 20, 2013 7:41 am
by jamesh
It's absolutely best to assume that if you see an odd bit of code, it's odd for a very specific reason. Usually to bypass a HW idiosyncrasy/bug - it's much cheaper to fix the software than the fix the hardware. I'm not sure of the exact reason for this particular case, but I bet if you removed one of the writes, things would start to behave erratically - probably when you least expect it. HW is like that. Especially very complex HW like this.

Re: Some more Broadcom C Codes.

Posted: Fri Dec 20, 2013 8:58 am
by jdb
jamesh wrote:It's absolutely best to assume that if you see an odd bit of code, it's odd for a very specific reason. Usually to bypass a HW idiosyncrasy/bug - it's much cheaper to fix the software than the fix the hardware. I'm not sure of the exact reason for this particular case, but I bet if you removed one of the writes, things would start to behave erratically - probably when you least expect it. HW is like that. Especially very complex HW like this.
In this case the software is idiosyncratic.

The ARM DSB and DMB instructions exist for this purpose - they ensure that outstanding reads/writes to external memory are complete before executing the next instruction. This function should have an asm dsb at the start and end of the call.

These instructions are evidently functional in 2835 as the USB FIQ does this on a regular basis - reads and writes USB peripheral registers even when the processor is in IRQ context.

The only remaining odd thing about the 2835 bus arch is a "feature" called early bResp, where AXI writes can be signalled to the processor as complete prior to the data arriving at the destination peripheral. This is somewhat important when considering the FIQ - I've seen it trigger twice on the same interrupt because the data (resetting the interrupt register) has still been in-flight when the processor exited from FIQ mode.

Re: Some more Broadcom C Codes.

Posted: Fri Dec 20, 2013 9:07 am
by joan
jdb wrote: ...
In this case the software is idiosyncratic.

The ARM DSB and DMB instructions exist for this purpose - they ensure that outstanding reads/writes to external memory are complete before executing the next instruction. This function should have an asm dsb at the start and end of the call.
...
It's a userland library. I don't think those instructions are available.

Re: Some more Broadcom C Codes.

Posted: Fri Dec 20, 2013 9:28 am
by jamesh
It's an interesting tradeoff - the complexity of adding the ARM instructions (if indeed possible - ref. Joan), vs simply writing the data twice.

Re: Some more Broadcom C Codes.

Posted: Fri Dec 20, 2013 10:16 am
by jdb
joan wrote:
jdb wrote: ...
In this case the software is idiosyncratic.

The ARM DSB and DMB instructions exist for this purpose - they ensure that outstanding reads/writes to external memory are complete before executing the next instruction. This function should have an asm dsb at the start and end of the call.
...
It's a userland library. I don't think those instructions are available.

Seems to work.
I was wrong about the actual instruction, though - in ARMv6 it's a MCR instruction.

Code: Select all

(gdb) disassemble main
Dump of assembler code for function main:
   0x00008390 <+0>:     push    {r11}           ; (str r11, [sp, #-4]!)
   0x00008394 <+4>:     add     r11, sp, #0
   0x00008398 <+8>:     nop                     ; (mov r0, r0)
   0x0000839c <+12>:    mov     r3, #0
   0x000083a0 <+16>:    mcr     15, 0, r3, cr7, cr10, {5}
   0x000083a4 <+20>:    mov     r3, #0
   0x000083a8 <+24>:    mov     r0, r3
   0x000083ac <+28>:    add     sp, r11, #0
   0x000083b0 <+32>:    pop     {r11}
   0x000083b4 <+36>:    bx      lr

This apparently runs fine as a userspace program. There is option for accessing subsets of the MCR register space from either USR or SVC mode.

Re: Some more Broadcom C Codes.

Posted: Fri Dec 20, 2013 10:40 am
by joan
jdb wrote:
joan wrote:
jdb wrote: ...
In this case the software is idiosyncratic.

The ARM DSB and DMB instructions exist for this purpose - they ensure that outstanding reads/writes to external memory are complete before executing the next instruction. This function should have an asm dsb at the start and end of the call.
...
It's a userland library. I don't think those instructions are available.

Seems to work.
I was wrong about the actual instruction, though - in ARMv6 it's a MCR instruction.

Code: Select all

(gdb) disassemble main
Dump of assembler code for function main:
   0x00008390 <+0>:     push    {r11}           ; (str r11, [sp, #-4]!)
   0x00008394 <+4>:     add     r11, sp, #0
   0x00008398 <+8>:     nop                     ; (mov r0, r0)
   0x0000839c <+12>:    mov     r3, #0
   0x000083a0 <+16>:    mcr     15, 0, r3, cr7, cr10, {5}
   0x000083a4 <+20>:    mov     r3, #0
   0x000083a8 <+24>:    mov     r0, r3
   0x000083ac <+28>:    add     sp, r11, #0
   0x000083b0 <+32>:    pop     {r11}
   0x000083b4 <+36>:    bx      lr

This apparently runs fine as a userspace program. There is option for accessing subsets of the MCR register space from either USR or SVC mode.
I thought I tried using those instructions from a userland process. I must have done something else or done something wrong.