cancel
Showing results for 
Search instead for 
Did you mean: 

Program determinism when toggling a pin

machinist
Associate III

Hello,

I have a strange behaviour that I cannot explain. It is regarding the determinism of running code.

My setup is simple. I have a save routine that save some setup in two seperate pages (two copys of same data). So my saveroutine consists of one write function that is called twice with different adressoffsets and then stores some 60 values. Nothing wild, just a simple copy.

Now I wanted to check how long the writeing takes. Naturally I would expect the write routine to take amount N time and the entire save 2xN (give or take a few µs). Interrupts are deactivated during the entire save.

Case 1: I set the pin at the beginning of the entire save and reset it at the end. I measure a time of 22 ms.

Case 2: I set the pin at the start of each of the two write routines and reset it at the end of each routine. I would have expectet two puleses of about 11 ms. But I get two puleses with 22 ms and a pause inbetween of 10 ms. Note that the only thing I changed in the code is the Line for changing pin state.

Case 3: I set the pin in the start of the entire save. Then reset it at the beginning of each write and set it again at the end of each write. Then reseting it at the end of the entire save. Now I get three small 0.7 µs peaks with 31 ms appart.

Case 4: To see how long a pin toggle takes I added some pintoggles at the end of Case 1. just some

   GPIOB->BRR = 1<<12;

   GPIOB->BSRR = 1<<12;

   GPIOB->BRR = 1<<12;

   GPIOB->BSRR = 1<<12;

   GPIOB->BRR = 1<<12;

   GPIOB->BSRR = 1<<12;

   GPIOB->BRR = 1<<12;

and then I got a 22 ms pulse (like in case 1). A 40 ms pause and then the three pulses with just ~28 ns inbetween.

So all four cases run the same code exept for the line of pin toggling. And all cases (except 1 and 4) give me totally different pulse patterns. How is this explained?

void save(void)
{
	uint32_t Interrupts0 = NVIC->Active[0];
	NVIC->Disable[0] = Interrupts0;
	uint32_t Interrupts1 = NVIC->Active[1];
	NVIC->Disable[1] = Interrupts1;
	GPIOB->BSRR = 1<<12;
	uint32_t temp1 = read(OFFSET_PAGE_1 + FADR_SAVE_INIT_DONE);
	uint32_t temp2 = read(OFFSET_PAGE_2 + FADR_SAVE_INIT_DONE);
	if(temp1 == 0xFFFFFFFF)
	{
		_save(OFFSET_PAGE_1);
		if(write_page == 0) _save(OFFSET_PAGE_2);
	}else if(temp2 == 0xFFFFFFFF){
		_save(OFFSET_PAGE_2);
		if(write_page == 0) _save(OFFSET_PAGE_1);
	}else if(temp1 > temp2){
		_save(OFFSET_PAGE_2);
		if(write_page == 0) _save(OFFSET_PAGE_1);
	}else{
		_save(OFFSET_PAGE_1);
		if(write_page == 0) _save(OFFSET_PAGE_2);
	}
	if(write_page != 0) error(CONTROL_ERROR_WRITE);
	GPIOB->BRR = 1<<12;
	GPIOB->BSRR = 1<<12;
	GPIOB->BRR = 1<<12;
	GPIOB->BSRR = 1<<12;
	GPIOB->BRR = 1<<12;
	GPIOB->BSRR = 1<<12;
	GPIOB->BRR = 1<<12;
	NVIC->Enable[1] = Interrupts1;
	NVIC->Enable[0] = Interrupts0;
}

2 REPLIES 2
Danish1
Lead II

What is write_page? Where, and when, might it change?

How strongly do you know that save() is only being called once in a given tests?

What is _save()?

Please show the precise source code for each of the tests - perhaps guarded by #if lines - so we can be sure there are no slip-ups in your pulsing of the pin.

What sort of memory are you saving to?

For example if it is EEPROM, what they often have is a page-mode where the data are quickly written to a page cache in RAM inside the EEPROM chip. And then (in the background, and slowly) the RAM cached values are copied to the non-volatile storage. And you don't see this slow write time -- unless you happen to try to read or write to the EEPROM.

Hope this helps,

Danish

machinist
Associate III

Thanks for the reply.

write_page is just for error detection. If one write in _save() fails the second page will not be written in case it failes too, so one page is valid always. But it also generates an error that did not get generated. So both pages get written ok. I am certain that it only gets called from one point. The _save() routine is where the data gets written to one of the pages selected by the parameter. Data is stored in flashmemory. Each write is secured by a busywait. We are also using write routines in our bootloader. That works very reliable. As this is a customer project I can not post all the code.

Case 1 was simply this:

void save(void)
{
	uint32_t Interrupts0 = NVIC->Active[0];
	NVIC->Disable[0] = Interrupts0;
	uint32_t Interrupts1 = NVIC->Active[1];
	NVIC->Disable[1] = Interrupts1;
	GPIOB->BSRR = 1<<12;
	uint32_t temp1 = read(OFFSET_PAGE_1 + FADR_SAVE_INIT_DONE);
	uint32_t temp2 = read(OFFSET_PAGE_2 + FADR_SAVE_INIT_DONE);
	if(temp1 == 0xFFFFFFFF)
	{
		_save(OFFSET_PAGE_1);
		if(write_page == 0) _save(OFFSET_PAGE_2);
	}else if(temp2 == 0xFFFFFFFF){
		_save(OFFSET_PAGE_2);
		if(write_page == 0) _save(OFFSET_PAGE_1);
	}else if(temp1 > temp2){
		_save(OFFSET_PAGE_2);
		if(write_page == 0) _save(OFFSET_PAGE_1);
	}else{
		_save(OFFSET_PAGE_1);
		if(write_page == 0) _save(OFFSET_PAGE_2);
	}
	if(write_page != 0) error(CONTROL_ERROR_WRITE);
	GPIOB->BRR = 1<<12;
	NVIC->Enable[1] = Interrupts1;
	NVIC->Enable[0] = Interrupts0;
}

Case 2 was this

void save(void)
{
	uint32_t Interrupts0 = NVIC->Active[0];
	NVIC->Disable[0] = Interrupts0;
	uint32_t Interrupts1 = NVIC->Active[1];
	NVIC->Disable[1] = Interrupts1;
	uint32_t temp1 = read(OFFSET_PAGE_1 + FADR_SAVE_INIT_DONE);
	uint32_t temp2 = read(OFFSET_PAGE_2 + FADR_SAVE_INIT_DONE);
	if(temp1 == 0xFFFFFFFF)
	{
		_save(OFFSET_PAGE_1);
		if(write_page == 0) _save(OFFSET_PAGE_2);
	}else if(temp2 == 0xFFFFFFFF){
		_save(OFFSET_PAGE_2);
		if(write_page == 0) _save(OFFSET_PAGE_1);
	}else if(temp1 > temp2){
		_save(OFFSET_PAGE_2);
		if(write_page == 0) _save(OFFSET_PAGE_1);
	}else{
		_save(OFFSET_PAGE_1);
		if(write_page == 0) _save(OFFSET_PAGE_2);
	}
	if(write_page != 0) error(CONTROL_ERROR_WRITE);
	NVIC->Enable[1] = Interrupts1;
	NVIC->Enable[0] = Interrupts0;
}

And the _save()

void _save(uint32_t offs)
{
	if((offs != 0) && (offs != 0x400)) return;
 
	GPIOB->BSRR = 1<<12;
	write_page = 0;
	_erase(offs);
	FLASH->CR |= ((uint32_t)0x00000001);
	write(offs + FADR_FOO, 	var_foo);
	write(offs + FADR_SAVE_INIT_DONE, FW_VERSION);
	if(write_page != 0)
	{
		_erase(offs);
		error(CONTROL_ERROR_WRITE);
	}
	FLASH->CR &= ((uint32_t)0x00001FFE);			// PG_Reset
	FLASH->CR |= ((uint32_t)0x00000080);			// Lock the Flash
	GPIOB->BRR = 1<<12;
}

Write is simple

void write(uint32_t adr, uint32_t val)
{
	uint32_t temp = 0;
	wait_busy();
	*(uint16_t*)(adr) = (uint16_t)(val&0xFFFF);
	wait_busy();
	*(uint16_t*)(adr+2) = (uint16_t)((val>>16)&0xFFFF);
	wait_busy();
	temp = read(adr);
	if(temp == val) return;
	write_page |= 0xF1;
	return;
}

in case 3 I set the pins like in case 1 and reset the pins in the _save() routine start and set them on the end of _save().

So its really no big deal. Yet the results on the pin are strange.