-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathromops.c
More file actions
502 lines (437 loc) · 15.2 KB
/
romops.c
File metadata and controls
502 lines (437 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
/*
romops.c - EEPROM and FLASH ROM routines - part of CBUS libraries for PIC 18F
Original CANACC8 assembler version (c) Mike Bolton
Modifications to EEPROM routines and conversion to C18 (c) Andrew Crosland
FLASH routines by (c) Chuck Hoelzen
Modifications, refinements & combine EEPROM and FLASH into one module (C) Pete Brownlow 2014-2017 software@upsys.co.uk
This work is licensed under the:
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
To view a copy of this license, visit:
http://creativecommons.org/licenses/by-nc-sa/4.0/
or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
License summary:
You are free to:
Share, copy and redistribute the material in any medium or format
Adapt, remix, transform, and build upon the material
The licensor cannot revoke these freedoms as long as you follow the license terms.
Attribution : You must give appropriate credit, provide a link to the license,
and indicate if changes were made. You may do so in any reasonable manner,
but not in any way that suggests the licensor endorses you or your use.
NonCommercial : You may not use the material for commercial purposes. **(see note below)
ShareAlike : If you remix, transform, or build upon the material, you must distribute
your contributions under the same license as the original.
No additional restrictions : You may not apply legal terms or technological measures that
legally restrict others from doing anything the license permits.
** For commercial use, please contact the original copyright holder(s) to agree licensing terms
This software is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
**************************************************************************************************************
Note: This source code has been written using a tab stop and indentation setting
of 4 characters. To see everything lined up correctly, please set your
IDE or text editor to the same settings.
******************************************************************************************************
For library version number and revision history see CBUSLib.h
Ported to XC8 by Ian Hogg 23/5/2017
*/
/**
* Flash routines hide the complexity of erasing and writing in pages.
* A buffer is kept of the current page being changed and then writing it back
* in a single operation. This reduces the number of writes to each page of flash
* and extending its life.
*/
#include "devincs.h"
#include "romops.h"
#include "EEPROM.h"
#include "hwsettings.h"
#include "module.h"
//#pragma romdata BOOTFLAG
//rom BYTE bootflag = 0;
#ifndef __XC8__
#pragma udata MAIN_VARS
#endif
FlashFlags flashFlags;
BYTE flashbuf[64]; // Assumes that Erase and Write are the same size
BYTE flashidx;
WORD flashblock; //address of current 64 byte flash block
#ifndef __XC8__
//#pragma code APP
#endif
// Internal function definitions
void writeFlashShort(void);
void writeFlashWithErase(void);
BYTE readFlashBlock(WORD flashAddr);
/**
* Initialise variables for Flash program tracking.
*/
void initRomOps(void)
{
flashFlags.asByte = 0;
flashblock = 0xFFFF;
}
/**
* Flash block write. flash 64 byte buffer.
* Fast write, this requires no 0 to 1 bit changes, only 1 to 0 bits are allowed.
* Or use write_flash_long with erase before write
*/
void writeFlashShort(void)
{
#ifdef __XC8__
TBLPTR = flashblock & ~(64 - 1); //force row boundary
di(); // disable all interrupts ERRATA says this is needed before TBLWT
for (unsigned char i=0; i<64; i++) {
TABLAT = flashbuf[i];
asm("TBLWT*+");
}
// Note from data sheet:
// Before setting the WR bit, the Table
// Pointer address needs to be within the
// intended address range of the 64 bytes in
// the holding register.
// So we put it back into the block here
TBLPTR = flashblock & ~(64 - 1);
EECON1bits.EEPGD = 1; // 1=Program memory, 0=EEPROM
EECON1bits.CFGS = 0; // 0=ProgramMemory/EEPROM, 1=ConfigBits
EECON1bits.FREE = 0; // No erase
EECON1bits.WREN = 1; // enable write to memory
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = TRUE;
ei(); // enable all interrupts
EECON1bits.WREN = FALSE;
#else
WORD ptr;
#ifndef CPUF18K
BYTE fwCounter;
#endif
ptr= (WORD)flashbuf;
// check we are only writing within the persistent data area
if ((flashblock < MIN_WRITEABLE_FLASH) || (flashblock > MAX_WRITEABLE_FLASH)) {
// should never reach here
// assert
return;
}
// Call back into the application to check if now is a good time to write the flash
// as the processor will be suspended for up to 2ms.
while (! isSuitableTimeToWriteFlash());
di(); // disable all interrupts
TBLPTR=flashblock;
TBLPTRU = 0x00; // just to make sure
FSR0=ptr;
#ifdef CPUF18K
flashidx=64; // K series processors can write 64 bytes in one operation
#else
for (fwCounter = 1; fwCounter <=2; fwCounter++ ) // 18F processors need two iterations of 32 bytes each
{
flashidx=32;
#endif
_asm
L_w:
MOVF POSTINC0,W,0
MOVWF TABLAT,0
TBLWTPOSTINC
DECF flashidx,F,1
BNZ L_w
TBLRDPOSTDEC //PUT IT BACK IN THE BLOCK
_endasm
EECON1bits.EEPGD = 1; // 1=Program memory, 0=EEPROM
EECON1bits.CFGS = 0; // 0=ProgramMemory/EEPROM, 1=ConfigBits
EECON1bits.FREE = 0; // No erase
EECON1bits.WREN = 1; // enable write to memory
EECON2 = 0x55; // write 0x55
EECON2 = 0xaa; // write 0xaa
EECON1bits.WR = 1; // start writing
EECON1bits.WREN = 0; // disable write to memory
_asm
TBLRDPOSTINC // Table pointer ready for next 32
_endasm
TBLPTRU = 0x00; // this will be set to 1 after writing 0xFFFF so reset it
#endif
#ifdef CPUF18F
}
#endif
ei(); // enable all interrupts
}
/**
* Flash block write. flash 64 byte buffer with block erase.
*/
void writeFlashWithErase(void)
{
// Call back into the application to check if now is a good time to write the flash
// as the processor will be suspended for up to 2ms.
while (! isSuitableTimeToWriteFlash());
// Erase block first
TBLPTR=flashblock;
TBLPTRU = 0x00; // just to make sure
EECON1bits.EEPGD = 1; // 1=Program memory, 0=EEPROM
EECON1bits.CFGS = 0; // 0=Program memory/EEPROM, 1=ConfigBits
EECON1bits.WREN = 1; // enable write to memory
EECON1bits.FREE = 1; // enable row erase operation
di(); // disable all interrupts
EECON2 = 0x55; // write 0x55
EECON2 = 0xaa; // write 0xaa
EECON1bits.WR = 1; // start erasing
ei(); // enable all interrupts
EECON1bits.WREN = 0; // disable write to memory
//Now write data to flash
writeFlashShort();
}
/**
* If the buffer has unwritten changes then write these out to Flash.
*/
void flushFlashImage( void )
{
if (flashFlags.modified)
{
if(flashFlags.zeroto1)
writeFlashWithErase();
else
writeFlashShort();
}
}
/**
* Flash block read. Read flash through a 64 byte buffer with write back management.
* valid:3 //must be 101 (5) to be valid
* loaded:1 // if buffer is loaded
* modified:1 //flag if buffer is modified
* zeroto1:1 //flag if long write with block erase
*/
/**
* Read a byte from flash. If the required buffer is currently loaded then use the value
* stored there since it may have been modified. Otherwise load the buffer from flash
* before returning the value.
* @param addr the address to be read from Flash
* @return the byte read from Flash
*/
BYTE readFlashBlock(WORD flashAddr)
{
WORD ptr;
// check we are only reading within the persistent data area
if ((flashAddr < MIN_WRITEABLE_FLASH) || (flashAddr > MAX_WRITEABLE_FLASH)) {
// should never reach here
// assert
return 0xff;
}
if(flashFlags.valid !=5)
{
flashFlags.asByte=5; //force reload
}
if(flashFlags.loaded && flashblock!=(flashAddr & 0XFFC0))
{
//detected access from a different block so we need to write this one (if it has been changed)
flushFlashImage();
flashFlags.asByte=5;
}
if(!flashFlags.loaded)
{
// load the buffer
flashblock = flashAddr & 0xFFC0;
#ifdef __XC8__
EECON1=0X80; // access to flash
TBLPTR = flashblock;
for (unsigned char i=0; i<64; i++) {
asm("TBLRD*+");
flashbuf[i] = TABLAT;
}
#else
//load the buffer
ptr= (WORD)flashbuf;
FSR0=ptr;
flashblock = flashAddr & 0XFFC0;
TBLPTR=flashblock;
TBLPTRU = 0x00; // just to make sure
EECON1=0X80;
flashidx=64;
_asm
// MOVLB FLASHBUFPAGE
READ_BLOCK:
TBLRDPOSTINC
NOP
MOVFF TABLAT,POSTINC0
DECF flashidx,F,1
BNZ READ_BLOCK
_endasm
#endif
TBLPTRU = 0x00; // this will be set to 1 after reading 0xFFFF so reset it
flashFlags.loaded = TRUE;
}
return flashbuf[flashAddr & 0X3F];
}
/**
* Write a byte to the FLASH image. You may need to flush current image to Flash if necessary.
* @param addr the destination address of the byte to be written
* @param data the data byte to be written
*/
void writeFlashImage(BYTE * addr, BYTE data)
{
unsigned char *offset;
// check we are only writing within the persistent data area
if (((WORD)addr < MIN_WRITEABLE_FLASH) || ((WORD)addr > MAX_WRITEABLE_FLASH)) {
// should never reach here
// assert
return;
}
if(flashFlags.valid !=5)
{
flashFlags.valid=5; //force reload
}
if (!flashFlags.loaded || flashblock!=((WORD)addr & 0XFFC0))
readFlashBlock((WORD)addr);
offset = &flashbuf[(WORD)addr & 0x3F];
if(data !=*offset)
flashFlags.modified=1;
if(data & ~*offset)
flashFlags.zeroto1=1;
*offset=data;
}
/**
* Write one byte and flush to flash.
* @param flashAddr the destination address of the byte to be written
* @param flashData the data byte to be written
*/
void writeFlashByte( BYTE * flashAddr, BYTE flashData )
{
writeFlashImage( flashAddr, flashData ); // Put data into memory image, if necessary flush image to flash first
flushFlashImage(); // Flush any changes
}
/**
* Write one word to the flash buffer.
* @param flashAddr the destination address of the byte to be written
* @param flashData the data word to be written
*/
void setFlashWord( WORD * flashAddr, WORD flashData )
{
writeFlashImage( (BYTE*)flashAddr, (BYTE)(flashData & 0x00FF) ); // Put LS byte into memory image, if necessary flush image to flash first
writeFlashImage( ((BYTE*)flashAddr)+1, (BYTE)(flashData >> 8) ); // Repeat for MSByte
}
/**
* Write a range of addresses to Flash.
* @param flashAddr
* @param bufferaddr
* @param bufferSize
*/
void setFlashBuffer( BYTE * flashAddr, BYTE *bufferaddr, BYTE bufferSize )
{
BYTE i;
for ( i=0; i<bufferSize; i++)
writeFlashImage( flashAddr+i, bufferaddr[i] );
}
// *************** EEPROM operations
/**
* Read a byte from data EEPROM.
* @param addr the address to be read
* @return the byte from EEPROM
*/
BYTE ee_read(WORD addr) {
while (EECON1bits.WR) // Errata says this is required
;
// EEADRH = addr >> 8; // High byte of address to read
SET_EADDRH(addr >> 8);
EEADR = addr & 0xFF; /* Low byte of Data Memory Address to read */
EECON1bits.EEPGD = 0; /* Point to DATA memory */
EECON1bits.CFGS = 0; /* Access program FLASH/Data EEPROM memory */
EECON1bits.RD = 1; /* EEPROM Read */
while (EECON1bits.RD)
;
#ifdef __XC8
asm("NOP"); /* data available after a NOP */
#else
_asm
nop
_endasm
#endif
return EEDATA;
}
/**
* Write one byte to data EEPROM.
* We verify at end and try again if necessary.
* @param addr the address to be written
* @param data the data to be written
*/
void ee_write(WORD addr, BYTE data) {
BYTE interruptEnabled;
interruptEnabled = geti(); // store current global interrupt state
do {
SET_EADDRH(addr >> 8); // High byte of address to write
EEADR = addr & 0xFF; /* Low byte of Data Memory Address to write */
EEDATA = data;
EECON1bits.EEPGD = 0; /* Point to DATA memory */
EECON1bits.CFGS = 0; /* Access program FLASH/Data EEPROM memory */
EECON1bits.WREN = 1; /* Enable writes */
di(); /* Disable Interrupts */
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;
#ifdef __XC8__
while (EECON1bits.WR)
;
//asm("BTFSC EECON1, 1"); // should wait until WR clears
#else
_asm nop
nop _endasm
#endif
if (interruptEnabled) { // Only enable interrupts if they were enabled at function entry
ei(); /* Enable Interrupts */
}
while (!EEIF)
;
EEIF = 0;
EECON1bits.WREN = 0; /* Disable writes */
} while (ee_read(addr) != data);
}
/**
* Read a WORD (16 bit) word from EEPROM.
* Data is stored in little endian format
* @param addr the address to be read
* @return the WORD from EEPROM
*/
WORD ee_read_short(WORD addr)
{
WORD ee_addr = addr;
WORD ret = ee_read(ee_addr++);
ret = ret | ((WORD)ee_read(ee_addr) << 8);
return ret;
}
/**
* Write a WORD (16 bit) data to EEPROM.
* Data is stored in little endian format.
* @param addr the address to be written
* @param data the data to be written
*/
void ee_write_short(WORD addr, WORD data) {
WORD ee_addr = addr;
ee_write(ee_addr++, (BYTE)(data&0xFF));
ee_write(ee_addr, (BYTE)(data>>8));
}
/**
* Read the DevId from the Config area
*/
#ifdef __XC8__
extern const WORD devId @0x3FFFFE;
#endif
WORD readCPUType( void ) {
#ifdef __XC8__
return devId;
#else
WORD id = *(far rom WORD*)0x3FFFFE;
// Reset the upper TBLPTR byte. If this isn't done the compiler generated code
// to read rom variables gets the wrong values as they are read from the wrong address.
TBLPTRU = 0;
return( id );
#endif
/*
TBLPTR = 0xFFFE;
TBLPTRU = 0x3F;
EECON1=0X80;
di();
_asm
TBLRDPOSTINC
_endasm
b1 = TBLAT;
_asm
TBLRDPOSTINC
_endasm
b2 = TBLAT;
ei();
return (WORD)b2<<8 | b1;
*/
}