Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

FB doublebuffering

Sat Aug 04, 2012 10:54 am

Hi I've been able to setup the framebuffer correctly via baremetal. Writing to it also works well, but I'm wondering if there is a way to use doublebuffering somehow. There is a way to swap the buffer pointer?

dom
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5282
Joined: Wed Aug 17, 2011 7:41 pm
Location: Cambridge

Re: FB doublebuffering

Sat Aug 04, 2012 11:08 am


Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Sat Aug 04, 2012 11:25 am

Thanks! So is it enough to change the y_offset value in the framebuffer structure or is it needed to send some messages to the mailbox?

User avatar
Cycl0ne
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am

Re: FB doublebuffering

Sat Aug 04, 2012 5:06 pm

just put the y_offset into your init message. and when you want to switch, just send a new message with: set offset_y

Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Mon Aug 06, 2012 2:34 pm

Ok, so , I've been trying what you suggested Cyclone. I'm trying to send the "Set virtual offset" message to the channel 8 (i think that this is the right message, right? Tag: 0x00048009)

I get confirmation from the mailbox that the message arrived and that it was correct, but the color on the screen is always the same.

I created a framebuffer of size 640x960 (double height) and I'm trying to offset the height to 480 from 0. Half the screen is filled with the color red , and the other half with the color green, but the screen remains always red.

This is my (kinda awful) prototyping code that I'm using:

Code: Select all

#define MAIL0_READ 0x2000b880
#define MAIL0_PEAK 0x2000b890
#define MAIL0_SENDER 0x2000b894
#define MAIL0_STATUS 0x2000b898
#define MAIL0_CONFIG 0x2000b89C
#define MAIL0_WRITE 0x2000b8a0
#define VC_OFF 0x40000000
#define SET_VAL(a, b) *((uint32_t*)(a)) = b;
#define GET_VAL(a) *((uint32_t*)(a))

typedef unsigned int uint32_t;
typedef unsigned char uint8_t;

extern void memory_barrier();
extern void light_led();
extern void shut_led();
extern void dummy(int a);

typedef struct FB {
  uint32_t width;
  uint32_t height;
  uint32_t virtual_width;
  uint32_t virtual_height;
  uint32_t pitch;
  uint32_t depth;
  uint32_t x_offset;
  uint32_t y_offset;
  uint32_t pointer;
  uint32_t size;
} FB;

typedef struct SetOffsetTag {
  uint32_t TagId;
  uint32_t BuffSize;
  uint32_t ReqRespInd;
  uint8_t Vals[8];
} SetOffsetTag;

typedef struct SetOffsetBuffer {
  uint32_t BufferSize;
  uint32_t BufferReqResp;
  SetOffsetTag OffTag;
  uint32_t EndTag;  
} SetOffsetBuffer;

volatile FB* frameb = (volatile FB*) 0x000010000;

volatile char* framebp;

void write_to_mailbox(char channel, uint32_t data) {
  volatile uint32_t* mail_base = (uint32_t*) MAIL0_STATUS;
  uint32_t read_val;

  do {
    memory_barrier();
    read_val = *mail_base;
  } while (read_val & 0x80000000);
  mail_base = (uint32_t*) MAIL0_WRITE;

  read_val = (uint32_t) (((data+VC_OFF) & 0xFFFFFFF0) | (channel & 0xF));
  *mail_base = read_val;
}

uint32_t read_from_mailbox() {
  volatile uint32_t* mail_base = (uint32_t*) MAIL0_STATUS;
  uint32_t read_val;

  do {
    memory_barrier();
    read_val = *mail_base;
  } while (read_val & 0x40000000);
  memory_barrier();
  mail_base = (uint32_t*) MAIL0_READ;
  read_val = *mail_base;
  return read_val;
}

int main() {
  volatile uint32_t* mail_base = (uint32_t*) MAIL0_STATUS;
  uint32_t read_val;
  unsigned int i, j;
  int switch_to_buffer = 0;
  int idx = 0;
  SetOffsetBuffer off_buff;

  frameb->width = 640;
  frameb->height = 960;
  frameb->virtual_width = frameb->width;
  frameb->virtual_height = frameb->height/2;
  frameb->pitch = 0;
  frameb->depth = 24;
  frameb->x_offset = 0;
  frameb->y_offset = 0;
  frameb->pointer = 0;
  frameb->size = 0;
  off_buff.BufferSize = sizeof(off_buff);
  off_buff.BufferReqResp = 0;
  off_buff.EndTag = 0;
  off_buff.OffTag.TagId = 0x00048009;
  off_buff.OffTag.BuffSize = 8;
  off_buff.OffTag.ReqRespInd = 0x8; 
  write_to_mailbox(1, (uint32_t) frameb);

read:
  read_val = read_from_mailbox();

  if (read_val != 0x00000001) 
    goto read;
  
  memory_barrier();

  framebp = (char*) (((uint32_t)frameb->pointer) - VC_OFF);

  for (i = 0; i < 480; ++i) {
    for (j = 0; j < 640; ++j) {
      int idx;
      idx = i*frameb->pitch + j*3;
      framebp[idx] = 255;
      framebp[idx+1] = 0;
      framebp[idx+2] = 0;
    }
  }
  framebp = (char*) ((((uint32_t)frameb->pointer) + 640*480*3) - VC_OFF);

  for (i = 0; i < 480; ++i) {
    for (j = 0; j < 640; ++j) {
      int idx;
      idx = i*frameb->pitch + j*3;
      framebp[idx] = 0;
      framebp[idx+1] = 255;
      framebp[idx+2] = 0;
    }
  }

    SET_VAL(off_buff.OffTag.Vals, 0)
    SET_VAL((off_buff.OffTag.Vals+4), 480)

    write_to_mailbox(8, (uint32_t) &off_buff);

read2:
    read_val = read_from_mailbox();

    if (((read_val & 0xF) != 0x00000008) && (read_val & 0xF) != ((uint32_t) &off_buff) & 0xF)
      goto read2;
    memory_barrier();

    if (off_buff.BufferReqResp != 0x80000000)
      return 1;
    if (off_buff.OffTag.ReqRespInd != 0x80000008)
      return 1;

    light_led();

  return 0;

}
In the end of the code I check if the answer from the mailbox is correct or not, and if it is correct I light up a led (the OK led, the one lighting up when the SD Card is been accessed) and if something in the answer is not correct I don't light up the led. The led is lighted up, so the answer seems to be correct.

Someone has an idea on why it doesn't switch to the other half of the screen?

User avatar
DexOS
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
Contact: Website

Re: FB doublebuffering

Mon Aug 06, 2012 5:34 pm

I do not use Dbuffer in this way, but i would try

Code: Select all

  frameb->width = 640;
  frameb->height = 480;
  frameb->virtual_width = frameb->width;
  frameb->virtual_height = frameb->height/2
And just change the "y_offset" to =480 in the strut.
This is just a guess, as i have not tried it.
Batteries not included, Some assembly required.

Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Mon Aug 06, 2012 5:45 pm

Thanks , but I guessed how to do it.

There were 2 errors in my code:

1) The buffer containing the tag to transfer to the Mailbox wasn't aligned to 16 bytes. (I used an automatic variable instead of a fixed address like I did for the framebuffer init structure)

2) I set the framebuffer height and virtual height incorrectly. I made the physical height 2 times bigger and the virtual height half the size of the physical height, while I had to make the virtual height twice the size of the physical height that had to remain the same.

Now it works and I have disco effect (switching between red and green very fast :p)

dom
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 5282
Joined: Wed Aug 17, 2011 7:41 pm
Location: Cambridge

Re: FB doublebuffering

Mon Aug 06, 2012 5:48 pm

I think you want the virtual_height to be 960 and the height to be 480.
Then y_offset=0 or 480 to swap between the two buffers.

Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Mon Aug 06, 2012 6:28 pm

Umm, I'M a little bit confused. From what you say it seems like if it isn't even needed to send a message to the mailbox to do the buffer exchange ... so, what is the right procedure?Change the value in the struct and then send some messages to the mailbox or not?

User avatar
DexOS
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
Contact: Website

Re: FB doublebuffering

Mon Aug 06, 2012 6:33 pm

Cool you got it working, i did mean * not / :oops:
Batteries not included, Some assembly required.

Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Mon Aug 06, 2012 6:44 pm

I got it working by sending a message to the mailbox using a message to channel 8 (the one I described above ) , but dex was talking about a different way by writing inside the framebuffer struct and I'm intetested about it . My method works , but doesn't seem like the most efficient

romell
Posts: 25
Joined: Mon Jul 23, 2012 6:57 pm

Re: FB doublebuffering

Mon Aug 06, 2012 6:53 pm

Kariddi wrote:I got it working by sending a message to the mailbox using a message to channel 8 (the one I described above ) , but dex was talking about a different way by writing inside the framebuffer struct and I'm intetested about it . My method works , but doesn't seem like the most efficient
I think it's possible yes, but from what I understand the old framebuffer channel (1) should be considered more or less deprecated since the introduction of the tags channel (8).

User avatar
Cycl0ne
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am

Re: FB doublebuffering

Mon Aug 06, 2012 8:46 pm

romell wrote:
Kariddi wrote:I got it working by sending a message to the mailbox using a message to channel 8 (the one I described above ) , but dex was talking about a different way by writing inside the framebuffer struct and I'm intetested about it . My method works , but doesn't seem like the most efficient
I think it's possible yes, but from what I understand the old framebuffer channel (1) should be considered more or less deprecated since the introduction of the tags channel (8).
Correct.

romell
Posts: 25
Joined: Mon Jul 23, 2012 6:57 pm

Re: FB doublebuffering

Mon Aug 06, 2012 8:53 pm

Kariddi wrote: 1) The buffer containing the tag to transfer to the Mailbox wasn't aligned to 16 bytes. (I used an automatic variable instead of a fixed address like I did for the framebuffer init structure)
You don't need a fixed address to get the correct alignment. Assuming you're using gcc, just put "__attribute__ ((aligned (16)))" after your struct declaration and the compiler will take care of the alignment (or more precisely, tell the linker to take care of it).

User avatar
DexOS
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
Contact: Website

Re: FB doublebuffering

Mon Aug 06, 2012 10:09 pm

I may cut out framebuffer coding on the PI and use the PI as a supper arduino dev board, as i am sick and tired of them changing stuff.
They should make sure to keep backward compatibility, its no fun re-coding once a month.
I have re-coded 3 times so far.
But then again, thats the price of being one of the first to get graphics working.
Last edited by DexOS on Mon Aug 06, 2012 10:12 pm, edited 1 time in total.
Batteries not included, Some assembly required.

User avatar
Cycl0ne
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am

Re: FB doublebuffering

Mon Aug 06, 2012 10:12 pm

DexOS wrote:I may cut out framebuffer coding on the PI and use the PI as a supper arduino dev board, as i am sick and tired of them changing stuff.
They should make sure to keep backward compatibility, its no fun re-coding once a month.
I have re-coded 3 times so far.
why you recoded? the old mailbox still works.. the new does it job too. and if you stay on the same blob image.. you dont need to care about new things breaking you. and for ppl using your bin, you can link in the correct blob from github.

romell
Posts: 25
Joined: Mon Jul 23, 2012 6:57 pm

Re: FB doublebuffering

Mon Aug 06, 2012 10:13 pm

DexOS wrote: ...its no fun re-coding once a month.
Not if you have to do it in assembly.

"Bazinga" :D

User avatar
DexOS
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
Contact: Website

Re: FB doublebuffering

Mon Aug 06, 2012 10:26 pm

Cycl0ne wrote:
DexOS wrote:I may cut out framebuffer coding on the PI and use the PI as a supper arduino dev board, as i am sick and tired of them changing stuff.
They should make sure to keep backward compatibility, its no fun re-coding once a month.
I have re-coded 3 times so far.
why you recoded? the old mailbox still works.. the new does it job too. and if you stay on the same blob image.. you dont need to care about new things breaking you. and for ppl using your bin, you can link in the correct blob from github.
The problem is you can not stay on the same blob, because somethings will need the new blob.
Example, when they release the camera, to use it from bare metal ( if possible) will need a updated blob.
I was for example testing a friend bare metal code, but it did not work as he had a older blob than me, but when he fix it it still did not work and he got the latest blob, i than had to fix my code to work on the latest blob.
Trust me most people want it to work on the latest blob.
And if they use my blob to test my code, then find linux or something does not work, they blame you thinking you have broke something.
Batteries not included, Some assembly required.

User avatar
DexOS
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
Contact: Website

Re: FB doublebuffering

Mon Aug 06, 2012 10:28 pm

romell wrote:
DexOS wrote: ...its no fun re-coding once a month.
Not if you have to do it in assembly.

"Bazinga" :D
:lol:
Batteries not included, Some assembly required.

Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Tue Aug 07, 2012 7:32 pm

I want to ask you guys about memory barriers. I read on the broadcom documentation that when starting communication with a peripheral you should barrier, at the moment i put barriers randomly everywhere just to be safe.Then i tried to remove all of them and everything continued to work . So, where and when should you put memory barriers in the code?

User avatar
mahjongg
Forum Moderator
Forum Moderator
Posts: 11995
Joined: Sun Mar 11, 2012 12:19 am
Location: South Holland, The Netherlands

Re: FB doublebuffering

Tue Aug 07, 2012 7:41 pm

In a very generic sense you put barriers between things that shouldn't be overwitten by crashing code and things that have a potential to overwrite code when crashing. But I suspect this might not be as helpful as I want it to be.

romell
Posts: 25
Joined: Mon Jul 23, 2012 6:57 pm

Re: FB doublebuffering

Tue Aug 07, 2012 8:03 pm

mahjongg wrote:In a very generic sense you put barriers between things that shouldn't be overwitten by crashing code and things that have a potential to overwrite code when crashing. But I suspect this might not be as helpful as I want it to be.
Hm, I'm pretty sure that wasn't the kind of barrier he asked about.. (whatever that kind is, I didn't understand much of what you wrote there :?).
The "Data Memory Barrier" and "Data Synchronization Barrier" are two ARM specific instructions. The ARM TRM also defines the term "Memory Barrier" as quote:
ARM TRM wrote:Memory barrier is the general term applied to an instruction, or sequence of instructions, used
to force synchronization events by a processor with respect to retiring load/store instructions in
a processor core. A memory barrier is used to guarantee completion of preceding load/store
instructions to the programmers model, flushing of any prefetched instructions prior to the
event, or both. The ARMv6 architecture mandates three explicit barrier instructions in the
System Control Coprocessor to support the memory order model, see the ARM Architecture
Reference Manual, and requires these instructions to be available in both Privileged and User
modes:
I'm not really sure what this means either. I tend to do pretty much the same as you Kariddi - put them where I think they might be needed, just to be safe.

I do this primarily when switching between writing from one peripheral to reading from another. I feel that this is where the risk of out-of-order data is greatest. In addition to the barrier instructions I usually also flush the caches.

It would be great if someone who really knows how this stuff works could clarify it a bit :)

Kariddi
Posts: 8
Joined: Fri Aug 03, 2012 10:32 pm

Re: FB doublebuffering

Tue Aug 07, 2012 8:36 pm

Thanks, Then I'll continue to be safe until more infos come out.ATM I do Data Synchronization Barriers and Prefetch and I also invalidate the instruction cache

User avatar
DexOS
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
Contact: Website

Re: FB doublebuffering

Tue Aug 07, 2012 8:53 pm

You need a Memory Barrier when
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.
Batteries not included, Some assembly required.

User avatar
Cycl0ne
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am

Re: FB doublebuffering

Tue Aug 07, 2012 8:55 pm

im still also not 100% sure about, since i also have seen three different approaches on that (3 different code parts), one from dexos, one from dwelch and one from tufy.

as far as i know, you should put it there, where you communicate with other devices. So:
- USB Registers
- MMC Registers
- READ/WRITE Data (DMA, USB, MMC,...) example here:
you say DMA READ 128bytes from somewhere. and when dma is finished, before reaching to this memory, do a barrier = flush caches, so that you are sure the correct data is in it.
- VideoCore

Everything you do with cpu + memory without the other devices, you dont need to.

Return to “Bare metal, Assembly language”