tags:

views:

72

answers:

1

Hello

I'm having problem to understand some piece of code in MTD driver

#define ROUNDUP(x, y)       ((((x)+((y)-1))/(y))*(y))
...
static struct mtd_partition my_parts[] =
{
   {
      .name = "boot",
      .size = 0,
      .offset = 0,
      .mask_flags = MTD_WRITEABLE
   },
   {
      .name = "linux",
      .size = 0,
      .offset = 0
   },
   {
       .name = "rootfs",
       .size = 0,
       .offset = 0,
       .mask_flags = MTD_WRITEABLE
   },
   {
       .name = "nvram",
       .size = 0,
       .offset = 0
   },
   {
       .name = 0,
       .size = 0,
       .offset = 0
   }
}
...

i = (sizeof(bcm947xx_parts)/sizeof(struct mtd_partition)) - 2;

bcm947xx_parts[i].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
bcm947xx_parts[i].offset = size - bcm947xx_parts[i].size;

So here are my questoins: 1) why is it necessary to round up the size of the partition? 2) could you help to understand how the rounding works? 3) flash driver in boot loader on the same platform doesn't do the rounding for this specific partition, so the flash layout has different offsets in the kernel side and in the bootloader. What is the reason for this?

Thanks in advance for any valuable comments !

+5  A: 

(1) Flash memory comes in multiples of its erase size. (Apparently. At least, this is what the quoted code tells me.) This means there is a gap between the end of the NVRAM and whatever comes next. This gap is less than the size of one erase size. In flash, it's convenient to not put two objects with different rewrite schedules in a single erase block -- changing either object requires the flash storage controller to copy the block to a temporary store, apply a partial update to the store, erase the block (slow-ish), and write the updated block to the main store. (It can reuse a different previously erased block and thread it back in the place of the original block. But this is considered a high tech optimization.)

(2) How to parse macros:

((((x)+((y)-1))/(y))*(y))

Step 1, remove the parens around the arguments that make sure that complicated expressions passed as arguments don't suddenly rebind in unexpected ways due to operator precedence.

(((x+(y-1))/y)*y)

Step 2, remove paranoid parens for operations that clearly have the indicated precedence.

(x+y-1)/y*y

Step 3, use your C parsing rules, not your algebra rules. If x and y are integral types (not enough information in your code to be certain of this), then the division is integer division, so translate from C to math.

 floor((x+y-1)/y)*y

Step 4, read. If x is a multiple of y, then since y-1 is too small to be a multiple of y, the operation just gives back x. If x is 1 more than a multiple of y, then the +y-1 pushes the numerator over the next multiple of y and the result is the smallest multiple of y that happens to be larger than x. In fact, if x is between 1 more and y-1 more than a multiple of y, the "+y-1" bumps the numerator up over the next multiple of y and the result of rounding up is the smallest multiple of y larger than x.

What we find, therefore is that ROUNDUP(x,y) rounds x up to the smallest multiple of y that happens to be greater than or equal to x. Additionally, this macro evaluates its second argument more than once: don't put expressions with side effects in the second slot unless you want those side effects to happen three times per call. (Consider int i = 3; ROUNDUP(6,i++) and wonder which subexpressions are evaluated before and which after each of the three increments of i.)

(3) No idea. No one told the bootloader writer that NVRAMs only come in multiples of erasesize?

Eric Towers