Is there an intrinsic limit to the width of the bit banding for peripherals for the
Cortex M3?
EFM32 Giant Gecko seems to have a limit of eight bits - bits 8 to 31 does not seem to work.
Is this normal?
The problem I have found is that when assigning a bit alias name to the position in bit alias memory that is supposed to represent one of the bits 8 to 31 in a peripheral register, that alias position does not work,while the alias positions representing any of bits 0 to 7 in the peripheral register work as expected.
It seems to be a Giant Gecko hardware bug.
From: yasuhikokoumoto <community@arm.com>
To: H van Rooyen <shapeshifter_100@yahoo.com>
Sent: Wednesday, March 2, 2016 2:51 AM
Subject: Re: - Cortex M3 peripheral Bit Banding limit?
Cortex M3 peripheral Bit Banding limit?
reply from yasuhikokoumoto in ARM Processors - View the full discussionHello shapeshifter, I am afraid you might be misunderstood.The bit banding area is so made that one bit is assigned into a word (i.e. 32 bit) address.Therefore you can handle a certain one bit by using 8 bit load or store instruction. The LSB of the 8 bit data of the LDRB/STRB will be valid. HTH,Yasuhiko Koumoto.
Reply to this message by replying to this email, or go to the message on ARM Connected Community
Start a new discussion in ARM Processors by email or at ARM Connected Community
Following Cortex M3 peripheral Bit Banding limit? in these streams: Inbox
Not interested in these emails anymore, or want to change how often they come? Update your email preferences. You will need to login to access your user preferences.
Hi there,
There can be several different reasons that bit band doesn't work:
Please note you don't need to use 8-bit load store instructions to access bit 8 to 31. You can use 32-bit/16-bit/8-bit load store instructions to access bit band (just make sure it is aligned transfer).
The choice of the instruction type affects the actual read write transfer size. If the peripheral only support 32-bit read/write, you should use 32-bit load/store instructions.
regards,
Joseph
Hi,
I understand all that, thanks - My defines were based on the ARM one that you point to, but using the predefined names provided by the EFM32 tools.
I have also tested by using a pointer to do the accesses with, using 32 bit writes - it does the same if you use 8 bit writes, as in the bit alias area, only the bit 0 has any relevance.
I have in the mean time tested the tiny gecko too, and the results are the same - bit banding does not work for bits 8 - 31 in the peripheral registers.
It looks like the implementation is broken.
If you have access to one of the EFM32 starter kits, and the free Silabs tools, you can try it and see for yourself.
It would be interesting to see if there are actually M3 implementations out there that fully support peripheral bit banding.
From: jyiu <community@arm.com>
Sent: Wednesday, March 2, 2016 11:34 AM
reply from Joseph Yiu in ARM Processors - View the full discussionHi there, There can be several different reasons that bit band doesn't work:
- Bit band is an optional feature on Cortex-M3/M4. If the option is not implemented, then it won't work. I don't think it is the case here because you said bit 0 to 7 works.
- If the bit band support is handled by C bad macro, it could be a problem of the C macro. An example C macro implementation can be found in ARM application note: Cortex-M3 Embedded Software Development
- MPU configuration blocking bit band alias (I don't think this is the case because it will trigger Memory Management Fault, so you should know that has happened.)
- The bus transfer size used is not supported by the peripheral (see below).
Please note you don't need to use 8-bit load store instructions to access bit 8 to 31. You can use 32-bit/16-bit/8-bit load store instructions to access bit band (just make sure it is aligned transfer).The choice of the instruction type affects the actual read write transfer size. If the peripheral only support 32-bit read/write, you should use 32-bit load/store instructions. regards,Joseph
Hi shapeshifter,
please let us show the concrete definitions for the bit specifying macros and their usage of the C program.
The descriptions of the C program might be wrong.
Best regards,
Yasuhiko Koumoto.
here is the cut and paste stuff:
// bitband helper "functions":
#define BITBAND_PERI(a,b) (((uint32_t) BITBAND_PER_BASE + ((uint32_t)&a-(uint32_t)PER_MEM_BASE) * (uint32_t)32 \ + (uint32_t)(b*4))) // Convert PERI address
#define BITBAND_RAM(a,b) (((uint32_t) BITBAND_RAM_BASE + ((uint32_t)&a-(uint32_t)SRAM_BASE) * (uint32_t)32 \ + (uint32_t)(b*4))) // Convert SRAM address
// define the int enable bits for timer0 cc0:
#define tim0_of_ie *((volatile unsigned char *)(BITBAND_PERI(TIMER0->IEN,0))) // int enables#define tim0_cc0_ie *((volatile unsigned char *)(BITBAND_PERI(TIMER0->IEN,4)))#define tim0_icbof0_ie *((volatile unsigned char *)(BITBAND_PERI(TIMER0->IEN,8)))
//Timer must be initialized, else nothing works if there is no clock
#include "em_timer.h"
//================================================================================// TIMER0_enter_DefaultMode_from_RESET//================================================================================extern void TIMER0_enter_DefaultMode_from_RESET(void) { // $[TIMER0 initialization] TIMER_Init_TypeDef init = TIMER_INIT_DEFAULT;
init.enable = 1; init.debugRun = 0; init.dmaClrAct = 1; init.sync = 0; init.clkSel = timerClkSelHFPerClk; init.prescale = timerPrescale1024; init.fallAction = timerInputActionNone; init.riseAction = timerInputActionNone; init.mode = timerModeUp; init.quadModeX4 = 0; init.oneShot = 0; init.count2x = 0; init.ati = 1; TIMER_Init(TIMER0, &init); // $
// $[TIMER0 CC0 init] TIMER_InitCC_TypeDef initCC0 = TIMER_INITCC_DEFAULT;
initCC0.prsInput = false; initCC0.prsSel = timerPRSSELCh0; initCC0.edge = timerEdgeBoth; initCC0.mode = timerCCModeCapture; initCC0.eventCtrl = timerEventEveryEdge; initCC0.filter = 0; initCC0.cofoa = timerOutputActionNone; initCC0.cufoa = timerOutputActionNone; initCC0.cmoa = timerOutputActionNone; initCC0.coist = 0; initCC0.outInvert = 0; TIMER_InitCC(TIMER0, 0, &initCC0); // $
// $[TIMER0 CC1 init] TIMER_InitCC_TypeDef initCC1 = TIMER_INITCC_DEFAULT;
initCC1.prsInput = false; initCC1.prsSel = timerPRSSELCh0; initCC1.edge = timerEdgeRising; initCC1.mode = timerCCModeOff; initCC1.eventCtrl = timerEventEveryEdge; initCC1.filter = 0; initCC1.cofoa = timerOutputActionNone; initCC1.cufoa = timerOutputActionNone; initCC1.cmoa = timerOutputActionNone; initCC1.coist = 0; initCC1.outInvert = 0; TIMER_InitCC(TIMER0, 1, &initCC1); // $
// $[TIMER0 CC2 init] TIMER_InitCC_TypeDef initCC2 = TIMER_INITCC_DEFAULT;
initCC2.prsInput = false; initCC2.prsSel = timerPRSSELCh0; initCC2.edge = timerEdgeRising; initCC2.mode = timerCCModeOff; initCC2.eventCtrl = timerEventEveryEdge; initCC2.filter = 0; initCC2.cofoa = timerOutputActionNone; initCC2.cufoa = timerOutputActionNone; initCC2.cmoa = timerOutputActionNone; initCC2.coist = 0; initCC2.outInvert = 0; TIMER_InitCC(TIMER0, 2, &initCC2); // $
// $[TIMER0 DTI init] TIMER_InitDTI_TypeDef initDTI = TIMER_INITDTI_DEFAULT;
initDTI.enable = 0; initDTI.activeLowOut = 0; initDTI.invertComplementaryOut = 0; initDTI.autoRestart = 0; initDTI.enablePrsSource = 0; initDTI.prsSel = timerPRSSELCh0; initDTI.prescale = timerPrescale1; initDTI.riseTime = 1; initDTI.fallTime = 1; initDTI.enableFaultSourceCoreLockup = 1; initDTI.enableFaultSourceDebugger = 0; initDTI.faultSourcePrsSel0 = 0; initDTI.faultSourcePrsSel0 = timerPRSSELCh0; initDTI.faultSourcePrsSel1 = 0; initDTI.faultSourcePrsSel1 = timerPRSSELCh0; initDTI.faultAction = timerDtiFaultActionInactive; initDTI.outputsEnableMask = 0 | TIMER_DTOGEN_DTOGCC0EN; TIMER_InitDTI(TIMER0, &initDTI); // $
}
// then the test:
tim0_cc0_ie = 1; // Set the channel 0 capture interrupt - works tim0_of_ie = 1; // timer 0 overflow interrupt set - works
tim0_icbof0_ie = 1; // capture buffer overflow interrupt set - does not work
// nothing wrong with the register hardware - this works:
TIMER_IntEnable(TIMER0, TIMER_IEN_OF | TIMER_IEN_CC0 | TIMER_IEN_ICBOF0);
Defining an alias for an I0 like this works, if the bit position is less than 8
#define start_chirp *((volatile unsigned char *)(BITBAND_PERI(GPIO->P[3].DOUT,4)))
Then you can write:
start_chirp = 1;
start_chirp = 0; // to set and clear the bit in DOUT, without using any ram at run time.
Such a bit will get out on the pin if it is enabled, but if the bit position is higher than seven, it fails.
Hope this helps!
Sent: Thursday, March 3, 2016 3:29 AM
reply from yasuhikokoumoto in ARM Processors - View the full discussionHi shapeshifter, please let us show the concrete definitions for the bit specifying macros and their usage of the C program.The descriptions of the C program might be wrong. Best regards,Yasuhiko Koumoto.
thank you for disclosing your codes.I think they might be correct.And I guess the root cause of the bit banding fail would be that the peripheral registers can be accessible only by 32bit-unit because they exist under the APB.As I don't know the bit banding mechanism, I cannot find by which size the actual access invoked by the bit banding for the peripheral registers.If it would be always byte-unit, the bits upper the number of 7 could not be accessed.If it would be followed by the access size for the bit band alias area, it might be successful when you changed
#define tim0_of_ie *((volatile unsigned char*)(BITBAND_PERI(TIMER0->IEN,0)))#define tim0_cc0_ie *((volatile unsigned char*)(BITBAND_PERI(TIMER0->IEN,4)))#define tim0_icbof0_ie *((volatile unsigned char*)(BITBAND_PERI(TIMER0->IEN,8)))
to
#define tim0_of_ie *((volatile uint32_t*)(BITBAND_PERI(TIMER0->IEN,0)))#define tim0_cc0_ie *((volatile uint32_t*)(BITBAND_PERI(TIMER0->IEN,4)))#define tim0_icbof0_ie *((volatile uint32_t*)(BITBAND_PERI(TIMER0->IEN,8)))
, but I am not certain.By the way, I think that bit accesses to SRAM by the bit banding will be successful even if the accessed bit number is bigger than 7.Is it correct?
Best regards,Yasuhiko Koumoto.
Thanks for taking the trouble to look.
Yes the bit banding in ram works as it stands, also for the higher order bits.
Changing the access to a 32 bit one makes no real difference - in the bit banding alias area, only bit 0 is implemented - thus writing 0x01, 0x011 or 0x0FFFFFFFF alldoes the same thing - it is the value of bit zero that is transferred into the relevantbit position.
One can also access the bits consecutively by using a pointer.
If "ptr" is a pointer to a 32 bit int, then you can write:
ptr = 0x420c0f00; // bit band address of PD DOUT register bit 0
then a loop containing:
*ptr++ = 1; // write the bit and point at next one
will set bits 0, 1, 2, 3... of DOUT register consecutively, one at a time.
if "ptr" is a pointer to char, then you have to write in the loop:
*ptr = 1; // write the bit
ptr += 4; // point at the next one
To do the same thing.
Thanks for looking - Silicon Systems is now also investigating
Sent: Friday, March 4, 2016 1:47 AM
reply from yasuhikokoumoto in ARM Processors - View the full discussionHi,
thank you for disclosing your codes.
I think they might be correct.
And I guess the root cause of the bit banding fail would be that the peripheral registers can be accessible only by 32bit-unit because they exist under the APB.
As I don't know the bit banding mechanism, I cannot find by which size the actual access invoked by the bit banding for the peripheral registers.
If it would be always byte-unit, the bits upper the number of 7 could not be accessed.
If it would be followed by the access size for the bit band alias area, it might be successful when you changed #define tim0_of_ie ((volatile unsigned char)(BITBAND_PERI(TIMER0->IEN,0)))
#define tim0_cc0_ie ((volatile unsigned char)(BITBAND_PERI(TIMER0->IEN,4)))
#define tim0_icbof0_ie ((volatile unsigned char)(BITBAND_PERI(TIMER0->IEN,8)))
to #define tim0_of_ie ((volatile uint32_t)(BITBAND_PERI(TIMER0->IEN,0)))
#define tim0_cc0_ie ((volatile uint32_t)(BITBAND_PERI(TIMER0->IEN,4)))
#define tim0_icbof0_ie ((volatile uint32_t)(BITBAND_PERI(TIMER0->IEN,8))) , but I am not certain.
By the way, I think that bit accesses to SRAM by the bit banding will be successful even if the accessed bit number is bigger than 7.
Is it correct?
Hi,can I ask one more question?If we assume the bit banding access to the peripheral registers is 8 bit size, I imagine both
tim0_of_ie = 1;
and
tim0_icbof0_ie = 1;
would be the same.
I am afraid the APB cannot distinguish 8 bit accesses (or it would be interpreted as 32 bit).That is, the "tim0_icbof0_ie = 1" would set IER[0].What is the fact?Because I have no Gecko MCUs, I cannot do the experiment.Although I have Kinetis MCU, almost all bits in the peripheral registers are located in the lower 8 bit range.Best regards,Yasuhiko Koumoto.
As I understand it, the hardware is kind of magic - there exists, for a register,another region in ram, where the bits in the register are represented as thebit 0 in consecutive 32 bit words.
So if the register lives at say addy1, there exists an array of 32 bit ints at anotheraddress, say addy2, where the relationship between addy1 and addy2 is fixed by the relationship between the Register region base address (addy1 is somewhere in this region) and the bit band alias region base address (addy2 is in this region)
Bit 0 of the first register in the register region has a representation at bit 0 of the first 32 bitword in the bit band alias region.
Then Bit 1 of the first of the registers has a representation at bit 0 of the second 32 bit word in the bit alias region.
and so on - there is a 32 bit word in the bit alias region for every bit in the register region, allfollowing consecutively on one another - four bytes for every bit, but only the first bit means anything.
And the magic is that when you write or read to or from any of these bit 0's in the alias region, then you actually access the corresponding bit in the register region. (Or in the ram - there issuch a word for each of the bits in the ram too, at a different place)
so setting tim0_of_ie =1; is writing to the bit band alias of the OF bit in the IEN register, bit 0 of the IEN register, that is bit 0 of the alias word at &tim0_of_ie
tim0_icbof0_ie = 1; similarly, this writes a one to the alias word that is supposed to be representing the bit 8 of the IEN register - but at a different place, namely at &tim0_icbof0_ie
and the relationship between the alias addresses is that tim_icbof0_ie lives at 8 * 4 = 32 bytes higher inthe memory than the place where tim0_of_ie lives, because OF is at bit 0 and ICBOF0 is at bit 8.
So no the accesses are not the same, they are at very different memory positions in the alias area, even though both of the bits in the register live at position zero of their respective bytes.
Does that answer your question, or have I misunderstood?
- Hendrik
Sent: Friday, March 4, 2016 9:19 AM
can I ask one more question?
If we assume the bit banding access to the peripheral registers is 8 bit size, I imagine bothtim0_of_ie = 1;andtim0_icbof0_ie = 1;would be the same.I am afraid the APB cannot distinguish 8 bit accesses (or it would be interpreted as 32 bit).
That is, the "tim0_icbof0_ie = 1" would set IER[0].
What is the fact?
Because I have no Gecko MCUs, I cannot do the experiment.
Although I have Kinetis MCU, almost all bits in the peripheral registers are located in the lower 8 bit range.
Hello Hendrik,
probably it would not be my answer.
The physical address of TIMER0->IEN is 0x4001000c.
I guess as the following.
"tim0_of_ie=1" will modify the bit 0 of the content of 0x4001000c.
"tim0_icbof0_ie=1" will modify the bit 0 of the content of 0x4001000d (this is not a typo).
However, because the TIMER0 and the other peripherals exist under APB via AHB-to-APB bridge in the Giant Gecko MCU, both accesses for 0x4001000c and 0x4001000d are interpreted as the access for 0x4001000c.
Therefore I predict both "tim0_of_ie=1" and "tim0_icbof0_ie=1" would set the bit 0 of 0x4001000c.
I would like to know whether it would be correct or not.
Thank you and best regards,
Hi Yasuhiko San,
Yes the bit 8 is supposed to affect the bit 0 at address 0x4001000d, via the bit bandingalias mechanism.
I have just tested it using a char access, and it does not write to bit 0 at address 0x40001000c,nor to bit 0 at 0x40001000d - just does nothing that I can see.
So its a more complex issue.
It turns out that the EFM32 micros need 32 bit accesses for the peripheral bit band alias area, else one gets the behaviour I have described.
If I use 32 bit accesses, then everything works as expected, and I can access the higher order bits individually via the bit banding access mechanism.
It is strange, because in the case of the ram bit banding, the char accesses work for all the bits.
So in a sense my immediate problem is solved - all I have to do is to use int instead of char, and everything starts working as expected.
Thanks for your interest anyway.
Sent: Friday, March 4, 2016 11:25 PM
reply from yasuhikokoumoto in ARM Processors - View the full discussionHello Hendrik, probably it would not be my answer.The physical address of TIMER0->IEN is 0x4001000c.I guess as the following."tim0_of_ie=1" will modify the bit 0 of the content of 0x4001000c."tim0_icbof0_ie=1" will modify the bit 0 of the content of 0x4001000d (this is not a typo).However, because the TIMER0 and the other peripherals exist under APB via AHB-to-APB bridge in the Giant Gecko MCU, both accesses for 0x4001000c and 0x4001000d are interpreted as the access for 0x4001000c.Therefore I predict both "tim0_of_ie=1" and "tim0_icbof0_ie=1" would set the bit 0 of 0x4001000c.I would like to know whether it would be correct or not. Thank you and best regards,Yasuhiko Koumoto.
Hi Hendrik,
thank you for your summary.
Can I confirm a thing?
Do you say
#define tim0_icbof0_ie *((volatile uint32_t*)(BITBAND_PERI(TIMER0->IEN,8)))
can access the bit 8 of the IEN register?
Otherwise, you can simply say you gave up the bit banding to access such bits as bigger than the bit 8 position?
Yasuhio Koumoto.
here is the macro I use:
#define tim0_icbof0_ie *((volatile unsigned int *)(BITBAND_PERI(TIMER0->IEN,8)))
then, in the setup routine for the interrupt I use the bit banding to access the bit:
tim0_icbof0_ie = 1; // capture buffer overflow interrupt set
And it just works, setting bit 8 of the IEN register of TIMER0 - if you change the int in the macro to a char, it does not work.
So yes using uint32_t will also work.
It makes no sense, it has to be peculiar to the EFM32 register bit band implementation, as char accesseswork for the bit banding in the ram.
shrug
Sent: Saturday, March 5, 2016 3:57 PM
reply from yasuhikokoumoto in ARM Processors - View the full discussionHi Hendrik,thank you for your summary.Can I confirm a thing?Do you say#define tim0_icbof0_ie ((volatile uint32_t)(BITBAND_PERI(TIMER0->IEN,8)))can access the bit 8 of the IEN register?Otherwise, you can simply say you gave up the bit banding to access such bits as bigger than the bit 8 position?Thank you and best regards,Yasuhio Koumoto.
I don't think so.It should be thought differently to access peripheral registers and SRAM.Commonly speaking, the peripheral registers are assumed by what access size can be accessible.In EFM32 case, the access size for the peripheral registers seems to have to be 32bit width.
This would not be a restriction but a specification.Therefore, the accesses for the bit band alias area of the peripheral registers would be 32bit width. That is, LDR/STR should be used.From the discussion till now, I have become a confident that actual register or SRAM area access size (i.e. 8. 16, 32bit) would be the same as the size for the bit band alias area.
For example,
case 1:#define tim0_icbof0_ie *((volatile unsigned int *)(BITBAND_PERI(TIMER0->IEN,8)))tim0_icbof0_ie = 1will act as 1) LDR r0,[r1] // r1 contains TIMER0->IER address2) ORR r0,r0,#(1<<8)3) STR r0,[r1]and case 2:#define tim0_icbof0_ie *((volatile unsigned short *)(BITBAND_PERI(TIMER0->IEN,8)))tim0_icbof0_ie = 1will act as 1) LDRH r0,[r1] // r1 contains TIMER0->IER address2) ORR r0,r0,#(1<<8)3) STRH r0,[r1]and case 3:#define tim0_icbof0_ie *((volatile unsigned char *)(BITBAND_PERI(TIMER0->IEN,8)))tim0_icbof0_ie = 1will act as 1) LDRB r0,[r1] // r1 contains TIMER0->IER address2) ORR r0,r0,#(1<<8)3) STRB r0,[r1]
I think I have completely understood the phenomenon.Also I have no further question.