Jump to content
Thermal Runaway

Multi-dimensional Arrays In Rom?

Recommended Posts

Hi everyone.

 

I'm trying to write something like this:

 

// BEGIN FONTS

rom unsigned char Font [26] [8]=

{

{0x05, 0x30, 0x0E, 0x09, 0x0E, 0x30, 0x00, 0x00}, // Code for char A

{0x04, 0x00, 0x3F, 0x25, 0x3F, 0x00, 0x00, 0x00}, // Code for char B

{0x05, 0x1E, 0x21, 0x21, 0x21, 0x12, 0x00, 0x00}, // Code for char C

{0x05, 0x00, 0x3F, 0x21, 0x21, 0x1E, 0x00, 0x00}, // Code for char D

{0x05, 0x00, 0x3F, 0x25, 0x25, 0x25, 0x00, 0x00}, // Code for char E

{0x04, 0x00, 0x3F, 0x05, 0x05, 0x00, 0x00, 0x00}, // Code for char F

{0x05, 0x1E, 0x21, 0x21, 0x29, 0x1A, 0x00, 0x00}, // Code for char G

{0x05, 0x00, 0x3F, 0x04, 0x04, 0x3F, 0x00, 0x00}, // Code for char H

{0x02, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00}, // Code for char I

{0x03, 0x30, 0x20, 0x3F, 0x00, 0x00, 0x00, 0x00}, // Code for char J

{0x06, 0x00, 0x3F, 0x08, 0x0C, 0x12, 0x21, 0x00}, // Code for char K

{0x04, 0x00, 0x3F, 0x20, 0x20, 0x00, 0x00, 0x00}, // Code for char L

{0x06, 0x00, 0x3F, 0x02, 0x04, 0x02, 0x3F, 0x00}, // Code for char M

{0x05, 0x3F, 0x02, 0x0C, 0x10, 0x3F, 0x00, 0x00}, // Code for char N

{0x05, 0x1E, 0x21, 0x21, 0x21, 0x1E, 0x00, 0x00}, // Code for char O

{0x05, 0x3F, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00}, // Code for char P

{0x05, 0x1E, 0x21, 0x29, 0x31, 0x2E, 0x00, 0x00}, // Code for char Q

{0x05, 0x3F, 0x05, 0x05, 0x0D, 0x37, 0x00, 0x00}, // Code for char R

{0x05, 0x12, 0x25, 0x29, 0x29, 0x1A, 0x00, 0x00}, // Code for char S

{0x05, 0x01, 0x01, 0x3F, 0x01, 0x01, 0x00, 0x00}, // Code for char T

{0x05, 0x1F, 0x20, 0x20, 0x20, 0x1F, 0x00, 0x00}, // Code for char U

{0x05, 0x03, 0x0C, 0x30, 0x0C, 0x03, 0x00, 0x00}, // Code for char V

{0x05, 0x1F, 0x20, 0x18, 0x20, 0x1F, 0x00, 0x00}, // Code for char W

{0x05, 0x21, 0x12, 0x0C, 0x12, 0x21, 0x00, 0x00}, // Code for char X

{0x05, 0x01, 0x06, 0x38, 0x06, 0x01, 0x00, 0x00}, // Code for char Y

{0x05, 0x31, 0x29, 0x25, 0x23, 0x21, 0x00, 0x00} // Code for char Z

} ;

// End Fonts

 

But the compiler complains that only 8-bit or 1-dimensional Arrays are permitted for ROM storage.

 

Is this correct or am I declaring it wrong?

 

If correct I'll have to go back to the drawing board with my program.

 

Thanks,

 

Brian

Share this post


Link to post
Share on other sites

ROM Arrays are limited to 8 bit types. I seem to recall that C implements multidimensional arrays as an array of arrays. Obviously any array cannot be fitted in 8 bits so no multidimensional ones are possible. I think there is also a further limitation to one page of ROM per array in the current version of the compiler.

 

This is *NOT* going to require a major rework of your code as if you strip all the inner {} from your definition so it reads

/ BEGIN FONTS
rom unsigned char Font []=
{0x05, 0x30, 0x0E, 0x09, 0x0E, 0x30, 0x00, 0x00, // Code for char A
...
0x05, 0x31, 0x29, 0x25, 0x23, 0x21, 0x00, 0x00 // Code for char Z
};
// End Fonts

it should compile OK and can then be accessed by Font[(x*8)|y] instead of Font[x][y]. As your second dimension is a power of two, left shift could be used instead to save a multiplication i.e, Font[(x<<3)|y] and depending on how good the compiler's optimisation actually is, may actually be what the first version generates. If you need to access more than 31 8x8 characters, you will need two or more font arrays and it will get a lot more ugly as ROM pointers are not supported.

Share this post


Link to post
Share on other sites

Hi Ian,

 

Thanks for your response. You are clearly far more adept at this than I, because I'm really struggling to see (at first glance) how Font[(x*8)|y] in your implementation would be equivalent to Font [x] [y] in my implementation!

 

I'll have to give that some consideration and see if I'm able to agree with it or not.

 

Thanks again for your help. In addition to seeing if I can get my head around I'm also interested to give it a try and see if it works!

 

Brian

 

 

ROM Arrays are limited to 8 bit types. I seem to recall that C implements multidimensional arrays as an array of arrays. Obviously any array cannot be fitted in 8 bits so no multidimensional ones are possible. I think there is also a further limitation to one page of ROM per array in the current version of the compiler.

 

This is *NOT* going to require a major rework of your code as if you strip all the inner {} from your definition so it reads

/ BEGIN FONTS
rom unsigned char Font []=
{0x05, 0x30, 0x0E, 0x09, 0x0E, 0x30, 0x00, 0x00, // Code for char A
...
0x05, 0x31, 0x29, 0x25, 0x23, 0x21, 0x00, 0x00 // Code for char Z
};
// End Fonts

it should compile OK and can then be accessed by Font[(x*8)|y] instead of Font[x][y]. As your second dimension is a power of two, left shift could be used instead to save a multiplication i.e, Font[(x<<3)|y] and depending on how good the compiler's optimisation actually is, may actually be what the first version generates. If you need to access more than 31 8x8 characters, you will need two or more font arrays and it will get a lot more ugly as ROM pointers are not supported.

Share this post


Link to post
Share on other sites

Ok Ian,

 

I have managed to convince myself that your proposal will work, but I have to admit that I don't think I would have come up with that technique myself! Very good!

 

The only thing I have left to convince myself of is the 31 character limit. I understand that the 31st character would be accessed by 24|7, but I don't understand why the array is limited to that size. If it is limited to 31 characters then this is a problem for supporting both upper and lower case fonts (not to mention special characters). I was hoping to write a function that would convert the ascii value of the string to be printed (one character at a time) to the relevant x and y coordinates of the array. I can't immediately see how this would be accomplished using more than one array.

 

I'll have a think about it.

 

Thanks again for the help,

 

Brian

 

 

 

ROM Arrays are limited to 8 bit types. I seem to recall that C implements multidimensional arrays as an array of arrays. Obviously any array cannot be fitted in 8 bits so no multidimensional ones are possible. I think there is also a further limitation to one page of ROM per array in the current version of the compiler.

 

This is *NOT* going to require a major rework of your code as if you strip all the inner {} from your definition so it reads

/ BEGIN FONTS
rom unsigned char Font []=
{0x05, 0x30, 0x0E, 0x09, 0x0E, 0x30, 0x00, 0x00, // Code for char A
...
0x05, 0x31, 0x29, 0x25, 0x23, 0x21, 0x00, 0x00 // Code for char Z
};
// End Fonts

it should compile OK and can then be accessed by Font[(x*8)|y] instead of Font[x][y]. As your second dimension is a power of two, left shift could be used instead to save a multiplication i.e, Font[(x<<3)|y] and depending on how good the compiler's optimisation actually is, may actually be what the first version generates. If you need to access more than 31 8x8 characters, you will need two or more font arrays and it will get a lot more ugly as ROM pointers are not supported.

Share this post


Link to post
Share on other sites

tut,

 

the reason for the 31 character limit is simple. I thought an Array of an 8-bit *type* simply meant that it had to be a char array, not that it was actually limited to 256 bits. If the latter, that explains the 31 character limit.

 

Thanks,

 

Brian

 

ROM Arrays are limited to 8 bit types. I seem to recall that C implements multidimensional arrays as an array of arrays. Obviously any array cannot be fitted in 8 bits so no multidimensional ones are possible. I think there is also a further limitation to one page of ROM per array in the current version of the compiler.

 

This is *NOT* going to require a major rework of your code as if you strip all the inner {} from your definition so it reads

/ BEGIN FONTS
rom unsigned char Font []=
{0x05, 0x30, 0x0E, 0x09, 0x0E, 0x30, 0x00, 0x00, // Code for char A
...
0x05, 0x31, 0x29, 0x25, 0x23, 0x21, 0x00, 0x00 // Code for char Z
};
// End Fonts

it should compile OK and can then be accessed by Font[(x*8)|y] instead of Font[x][y]. As your second dimension is a power of two, left shift could be used instead to save a multiplication i.e, Font[(x<<3)|y] and depending on how good the compiler's optimisation actually is, may actually be what the first version generates. If you need to access more than 31 8x8 characters, you will need two or more font arrays and it will get a lot more ugly as ROM pointers are not supported.

Share this post


Link to post
Share on other sites

Okay I tried it in practice and, whilst the compiler accepts it without issue, the optimisation stage complains that there is not enough space on the ROM for 208 bytes, when clearly there is (I have 2K ROM available).

 

I had a quick search on the forum and found that someone else has had the same issue. The solution (for my case) is to write:

 

rom char* Font = {........} ;

 

For some reason the optimisation stage still complains if you specify the size of the array, like this:

 

rom char* Font [208] = {........} ;

 

or even if you write this:

 

rom char* Font[] = {........};

 

Brian

Share this post


Link to post
Share on other sites
Okay I tried it in practice and, whilst the compiler accepts it without issue, the optimisation stage complains that there is not enough space on the ROM for 208 bytes, when clearly there is (I have 2K ROM available).

 

I had a quick search on the forum and found that someone else has had the same issue. The solution (for my case) is to write:

 

rom char* Font = {........} ;

Yes that works. rom char Font[]={... *should* be equivalent but something funny seems to be happening. Oddly enough if you shorten it considerably it compiles and links OK

 

The need to use char *Font instead of char font[] in the declaration Is IMHO a bug and may be worth reporting if no-one else has done so.

 

For some reason the optimisation stage still complains if you specify the size of the array, like this:

 

rom char* Font [208] = {........} ;

 

or even if you write this:

 

rom char* Font[] = {........};

 

I'm not surprised, you have just declared an array of char POINTERS and I don't think a pointer which can access the whole of a PIC's RAM *can* be an 8 bit type + its hitting the same bug in defining rom arrays!

Hopefully this will all be resolved in Version 7.

Share this post


Link to post
Share on other sites

Thanks for your help Ian.

 

I've now got my code working and can print to a GLCD (hacked from a nokia 7110 fyi) using;

 

lcd_print ("put string here") ;

 

The only thing is, because I can't fit an entire font into one array, I need to modify my lcd_print () function so that it can deal with separate arrays for upper or lower case characters, or special characters, depending on what it encounters as it steps through the string. I already have some ideas about that and I don't think it'll be too much of a problem.

 

Of course, the situation becomes more complicated if I want to support a couple of different fonts because then I'll need to group fonts into sets of three arrays and design my function not only to select one of three arrays depending on the character it encounters as it steps through the string, but also different groups of fonts arrays depending on the font number that is passed to it.

 

I'll see what I can do!

 

Brian

Share this post


Link to post
Share on other sites

Are you using a PIC that can reprogram its own flash? If you are, then you can read any ROM address directly *and* fetch arbitrary data which would bypass the ROM array/table size limits.

 

You also can reduce the font ROM usage by nearly 50%. A normal table uses RETLW instructions to store 8 bit values in one 14 or 16 bit word. Since one almost always wants a blank column and a blank row at the edges of the character cell, 7 bit data is fine, which on a 14 bit PIC16xx would let you pack two rows per ROM word. On a PIC18, you have 16 bit ROM words so can store two rows of 8 bits each with no compromises.

 

 

Getting the data into the ROM may be a bit awkward though.

Predicting where a ROM array gets put would be very difficult and probably very fragile so concatenating them is not practical and #pragma DATA would probably be the way to go. Unfortunately #pragma DATA is rather dumb about expression evaluation at compile time and the address can only be expressed as an number or a #define that substitutes a number. Offset addressing is definitely illegal. It might well be advisable to write a script or program in whatever language you are happiest in to generate a 'fonts.c' consisting of all the #pragma DATA statements which could then be included.

Share this post


Link to post
Share on other sites

Aha yes, I think I see where you're going with that. So I could store the required data for the fonts in ROM using #pragma DATA and, provided I know the address that the Font begins, I can then access it without requirement for an Array. I'll look into that and see if I can do it (I'm fairly new to this!).

 

This method would also be useful for retrieving and displaying bitmap image data, which I had actually been wondering about.

 

Your comments on offset addressing (and the illegality of it) seem to suggest that I'm not going to be able to implement this in the way I was thinking, though. I was hoping that I could write a function that scans through the string to be printed and calculates the address that the required font data lives at (based on the ascii value of the character being "scanned" and a known start address for the font data). I'm not entirely sure how this would be done, if indeed it is possible at all - I'll have to look into it.

 

If I can use that technique it would certainly simplify things, and make for a less messy program (the latter of which I find most important of all!)

 

Thanks,

 

Brian

 

 

 

 

 

Are you using a PIC that can reprogram its own flash? If you are, then you can read any ROM address directly *and* fetch arbitrary data which would bypass the ROM array/table size limits.

 

You also can reduce the font ROM usage by nearly 50%. A normal table uses RETLW instructions to store 8 bit values in one 14 or 16 bit word. Since one almost always wants a blank column and a blank row at the edges of the character cell, 7 bit data is fine, which on a 14 bit PIC16xx would let you pack two rows per ROM word. On a PIC18, you have 16 bit ROM words so can store two rows of 8 bits each with no compromises.

 

 

Getting the data into the ROM may be a bit awkward though.

Predicting where a ROM array gets put would be very difficult and probably very fragile so concatenating them is not practical and #pragma DATA would probably be the way to go. Unfortunately #pragma DATA is rather dumb about expression evaluation at compile time and the address can only be expressed as an number or a #define that substitutes a number. Offset addressing is definitely illegal. It might well be advisable to write a script or program in whatever language you are happiest in to generate a 'fonts.c' consisting of all the #pragma DATA statements which could then be included.

Share this post


Link to post
Share on other sites

It is surprising just how many way you can put stuff into rom and still get the same result.

rom char *Font1 = {1,2,3,4,5,6,7,8,9,11};
027C  0E00		  MOVLW 0x00
027E  6E2F		  MOVWF gbl_Font1

rom char Font2[] = {1,2,3,4,5,6,7,8,9,12};
0280  0E01		  MOVLW 0x01
0282  6E05		  MOVWF gbl_Font2

rom char *Font3[] = {1,2,3,4,5,6,7,8,9,13};
0284  0E02		  MOVLW 0x02
0286  6E0F		  MOVWF gbl_Font3

rom char Font4[4] = {1,2,3,4,5,6,7,8,9,14};
0288  0E03		  MOVLW 0x03
028A  6E25		  MOVWF gbl_Font4

rom char Font5[]@0x200 = {1,2,3,4,5,6,7,8,9,15};
028C  0E04		  MOVLW 0x04
028E  0102		  MOVLB 0x02
0290  6F00		  MOVWF gbl_Font5, 1

rom char *Font6@0x300 = {1,2,3,4,5,6,7,8,9,16};
0292  0E05		  MOVLW 0x05
0294  0103		  MOVLB 0x03
0296  6F00		  MOVWF gbl_Font6, 1

 

The last two which specify an absolute address loads up the BSR register to the correct page for no reason and does not put the data at the specified address.

 

Maybe all this is for things to come. Hmmmmmmmmm. ;)

 

The target PIC is 18F2520.

 

Cheers

 

Reynard

Share this post


Link to post
Share on other sites
...Unfortunately #pragma DATA is rather dumb about expression evaluation at compile time and the address can only be expressed as an number or a #define that substitutes a number. Offset addressing is definitely illegal...

 

This is easy to fix (at least for expressions that use addition and this is probably how offset is used most of the time). Will add into our todo list for 7.0 release.

 

Pavel

Share this post


Link to post
Share on other sites
...Unfortunately #pragma DATA is rather dumb about expression evaluation at compile time and the address can only be expressed as an number or a #define that substitutes a number. Offset addressing is definitely illegal...

 

This is easy to fix (at least for expressions that use addition and this is probably how offset is used most of the time). Will add into our todo list for 7.0 release.

 

Pavel

While you are looking at it, what about a couple of nice to have features?

1. A preprocessor macro _NEXT_ that returns the address after the last location stored by the preceding #pragma DATA.

Usage:

#define BLOCK1 0x1000
#pragma DATA BLOCK1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
#define BLOCK2 _NEXT_
#pragma DATA _NEXT_, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19

giving BLOCK2 the numeric value 0x100A and storing the data consecutively, without having to track how many words the first #pragma DATA stored. I know the address of an object isn't generally available till link time, but surely #pragma DATA is a special case due to it's absolute addressing. of course, you may wish to use some other name than _NEXT_ :-)

 

2. Some way of defining packed data. At the moment, we know it is handling word length parameters as #pragma DATA _CONFIG works, but what about #pragma DATAP to pack two bytes per word on PIC18 and two 7 bit chars per word on PIC16.

Usage:

#pragma DATAP 0x1000, "A test string", 0, 0x0D

would be equivalent to (PIC18)

#pragma DATA 0x1000, 0x2041, 0x6574, 0x7473, 0x7320, 0x7274, 0x6E69, 0x0067, 0x000D

Note: It's little endian to facilitate access on PIC18 and the 0 got packed with the string then the 0x0D got padded with 0x00 to fill the last word.

On PIC16 it would be

#pragma DATA 0x1000, 0x1041, 0x32F4, 0x3A73, 0x39A0, 0x3974, 0x3769, 0x0067, 0x000D

i.e. word=(char1 & 0x7F) | ((char2 &0x7F)<<7)

It might as well also be little endian as a PIC16 doesn't care.

See the MPASM data directive for an implementation of the same concept.

 

These with the addition of your proposed offset addressing, would go a *LONG* way to permitting efficient ROM storage of large amounts of text data without having to use 3rd party tools to get it there.

 

I don't see the need for a #pragma DATAT directive to back the high bytes with the RETLW opcode as I assume you will have the bugs out of rom char* with long strings by then, possibly even supporting a 'rom far char*' type that can handle arrays over 256 bytes long and maybe even allow uninitialised 'rom far' pointers and pointer assignment removing the current restrictions on their usage.

Edited by IanM

Share this post


Link to post
Share on other sites
ROM Arrays are limited to 8 bit types. I seem to recall that C implements multidimensional arrays as an array of arrays. Obviously any array cannot be fitted in 8 bits so no multidimensional ones are possible. I think there is also a further limitation to one page of ROM per array in the current version of the compiler.

 

This is *NOT* going to require a major rework of your code as if you strip all the inner {} from your definition so it reads

/ BEGIN FONTS
rom unsigned char Font []=
{0x05, 0x30, 0x0E, 0x09, 0x0E, 0x30, 0x00, 0x00, // Code for char A
...
0x05, 0x31, 0x29, 0x25, 0x23, 0x21, 0x00, 0x00 // Code for char Z
};
// End Fonts

it should compile OK and can then be accessed by Font[(x*8)|y] instead of Font[x][y]. As your second dimension is a power of two, left shift could be used instead to save a multiplication i.e, Font[(x<<3)|y] and depending on how good the compiler's optimisation actually is, may actually be what the first version generates. If you need to access more than 31 8x8 characters, you will need two or more font arrays and it will get a lot more ugly as ROM pointers are not supported.

 

Should Font[(x<<3)|y] actually be Font[(x<<3) + y] since its character (x<<3) plus offset into character (+y) ?

Share this post


Link to post
Share on other sites
...to pack two bytes per word on PIC18 and two 7 bit chars per word on PIC16.

 

You can indeed fill the PIC16/12 flash that way, but you will never be able to read it back,

because these devices lack the instructions to do so...

 

The only way obtaining a value from flash is a look-up table with a

 

retlw <c, 8bit>

 

instruction, which has the opcode (e.g. 12f...) 11 01 xx cccccccc.

 

If you change the upper bits, this will result in a different opcode than RETLW,

which will probably not do what you want it to do ;-)

 

 

I discovered the SourceBoost compiler just ~2 days ago and came here to

find out more about why the developers built in the 255 byte array limitation...

Probably laziness? ;))

 

This is somehow confusing.

Even a 12/16 PIC can use >8 (13) bit lookup tables by combining PCLATH and PCL

and every other compiler I know (HiTech, SDCC, CDC, ...) has this already

built in.

 

 

HP

Share this post


Link to post
Share on other sites
...

it should compile OK and can then be accessed by Font[(x*8)|y] instead of Font[x][y]. As your second dimension is a power of two, left shift could be used instead to save a multiplication i.e, Font[(x<<3)|y] and depending on how good the compiler's optimisation actually is, may actually be what the first version generates. If you need to access more than 31 8x8 characters, you will need two or more font arrays and it will get a lot more ugly as ROM pointers are not supported.

 

Should Font[(x<<3)|y] actually be Font[(x<<3) + y] since its character (x<<3) plus offset into character (+y) ?

It doesn't make any difference in this case whether you add or bitwise OR as the low 3 bits of the shifted value are zero. Either will work as long as x and y are in range.

 

The OR *may* generate more efficient code as the compiler wont be doing a carry to the high byte (char length values in C expressions get implicitly promoted to int during evaluation).

 

If you were using a second dimension YD that was *NOT* divisible by two, then it would need to be Font[x*YD+y] and the OR would *NOT* work.

Share this post


Link to post
Share on other sites
It doesn't make any difference in this case whether you add or bitwise OR as the low 3 bits of the shifted value are zero. Either will work as long as x and y are in range.

 

The OR *may* generate more efficient code as the compiler wont be doing a carry to the high byte (char length values in C expressions get implicitly promoted to int during evaluation).

 

If you were using a second dimension YD that was *NOT* divisible by two, then it would need to be Font[x*YD+y] and the OR would *NOT* work.

 

Ah yes i see. I still prefer the add rather than the OR, seems to make more sense for the dumber programmers (me!)

Share this post


Link to post
Share on other sites
...to pack two bytes per word on PIC18 and two 7 bit chars per word on PIC16.

 

You can indeed fill the PIC16/12 flash that way, but you will never be able to read it back,

because these devices lack the instructions to do so...

WRONG!

See section 3.5 of the PIC16F88 data sheet

PIC16F87/88

3.5 Reading Flash Program Memory

To read a program memory location, the user must

write two bytes of the address to the EEADR and

EEADRH registers, set the EEPGD control bit

(EECON1<7>) and then set control bit RD

(EECON1<0>). Once the read control bit is set, the

program memory Flash controller will use the second

instruction cycle to read the data. This causes the

second instruction immediately following the “BSF

EECON1,RD” instruction to be ignored. The data is

available in the very next cycle in the EEDATA and

EEDATH registers; therefore, it can be read as two

bytes in the following instructions. EEDATA and

EEDATH registers will hold this value until another read

or until it is written to by the user (during a write

operation).

EXAMPLE 3-3: FLASH PROGRAM READ

BANKSEL EEADRH; Select Bank of EEADRH
MOVF ADDRH, W;
MOVWF EEADRH; MS Byte of Program
; Address to read
MOVF ADDRL, W;
MOVWF EEADR; LS Byte of Program
; Address to read
BANKSEL EECON1; Select Bank of EECON1
BSF EECON1, EEPGD; Point to PROGRAM
; memory
BSF EECON1, RD; EE Read
;
NOP; Any instructions
; here are ignored as
NOP; program memory is
; read in second cycle
; after BSF EECON1,RD
BANKSEL EEDATA; Select Bank of EEDATA
MOVF EEDATA, W; DATAL = EEDATA
MOVWF DATAL;
MOVF EEDATH, W; DATAH = EEDATH
MOVWF DATAH;

 

Selected PIC16s have had this ability ever since the launch of the PIC16f877.

I agree it wont be any use on PIC12 parts though.

 

The only way obtaining a value from flash is a look-up table with a

 

retlw <c, 8bit>

 

instruction, which has the opcode (e.g. 12f...) 11 01 xx cccccccc.

 

If you change the upper bits, this will result in a different opcode than RETLW,

which will probably not do what you want it to do ;-)

Only if you are silly enough to call it. No problem if reading it directly.

I discovered the SourceBoost compiler just ~2 days ago and came here to

find out more about why the developers built in the 255 byte array limitation...

Probably laziness? ;))

 

This is somehow confusing.

Even a 12/16 PIC can use >8 (13) bit lookup tables by combining PCLATH and PCL

and every other compiler I know (HiTech, SDCC, CDC, ...) has this already

built in.

Probably NOT laziness. Handling extended ROM pointers would be a lot slower and take a lot more code space. Limiting ROM pointers to a 256 byte offset is reasonable as most mid-range applications don't have large quantities of constant data. I am agitating for 'rom far char *' support, and also ideally support for other types, but I do *NOT* want to see rom far pointers as the default as the performance impact will be considerable.

 

IMHO anyone calling Dave & Pavel lazy should first wait for V7.0x (lets give them the benefit of a couple of 'bugfix' releases past 7.00) and then be prepared to eat crow and apologise . . .

Edited by IanM

Share this post


Link to post
Share on other sites
...to pack two bytes per word on PIC18 and two 7 bit chars per word on PIC16.

 

You can indeed fill the PIC16/12 flash that way, but you will never be able to read it back,

because these devices lack the instructions to do so...

WRONG!

See section 3.5 of the PIC16F88 data sheet

 

Ouch, my eyes!

 

Incredible!

After almost 15 years of hacking through the 12/16 series, I must admit

that I totally missed these devices!

 

As of today, I will recall you as "IanMemory" ;)

 

Incredible...

 

 

Probably NOT laziness. Handling extended ROM pointers would be a lot slower and take a lot more code space. Limiting ROM pointers to a 256 byte offset is reasonable as most mid-range applications don't have large quantities of constant data. I am agitating for 'rom far char *' support, and also ideally support for other types, but I do *NOT* want to see rom far pointers as the default as the performance impact will be considerable.

 

Well, actually this limit was the reason for me to cancel my, already placed, SourceBoost order...

 

And this is exactly one of the n reasons why people usually switch over from asm to C.

While coding in asm is pretty straightforward and easy on 12/16 PICs, it is getting

more "complicated" (time consuming) if large amount of >8 bit types and storage classes are involved...

 

Except for this limitation, SourceBoost has the right price and performance between SDCC and HiTech's PRO version.

 

 

BTW.:

For arrays <256 bytes nothing would change. The original, limited implemenation could still be used...

 

IMHO anyone calling Dave & Pavel lazy should first wait for V7.0x (lets give them the benefit of a couple of 'bugfix' releases past 7.00) and then be prepared to eat crow and apologise . . .

 

Ok, I'll wait ;-))

 

 

HP

Share this post


Link to post
Share on other sites

You can indeed fill the PIC16/12 flash that way, but you will never be able to read it back,

because these devices lack the instructions to do so...

WRONG!

See section 3.5 of the PIC16F88 data sheet

 

Ouch, my eyes!

Sorry about the *BIG* *FONT*. It is a little hard on the eyes at 7AM and I could have been more diplomatic (one of my many failings) . . . .

I've gone back and edited it down two notches to 'size=5' just for you ;-)

Incredible!

After almost 15 years of hacking through the 12/16 series, I must admit

that I totally missed these devices!

 

As of today, I will recall you as "IanMemory" ;)

 

Incredible...

Well it was 'part of the package' with the ICD support that premiered in the PIC16F87x range and you can expect any part there is a bootloader for or that has native ICD support (no fragile & expensive debug header required) to have ROM read and write. The implementation of the write function varies across the family enough to be annoying though . . .

 

As the ICD support feature is *NOT* available in the PIC16 family parametric search, and I usually want parts my PICKit 2 can single step on a breadboard, I select 'Self-write = Yes', along with 'in Production' and all the PDIP + SPDIP package options so I can be sure I can actually get a chip and plug it into a breadboard. I have yet to be disappointed when I actually check the data sheet. That search is currently returning 12 out of 66 PIC16 devices.

 

One thing to be aware of: If your designing something for an electrically noisy or high RF environment, prototype on a Self-Write ICD capable PIC but switch to one without the feature for reliability before going to production or you may be unlucky enough to have devices erasing them self in the field :-( There are ways of mitigating the problem but in robotics applications for example, if the PIC lobotomises itself while in motion, it can get expensive.

 

... Handling extended ROM pointers would be a lot slower and take a lot more code space. Limiting ROM pointers to a 256 byte offset is reasonable as most mid-range applications don't have large quantities of constant data. I am agitating for 'rom far char *' support, and also ideally support for other types, but I do *NOT* want to see rom far pointers as the default as the performance impact will be considerable.

 

Well, actually this limit was the reason for me to cancel my, already placed, SourceBoost order...

 

And this is exactly one of the n reasons why people usually switch over from asm to C.

While coding in asm is pretty straightforward and easy on 12/16 PICs, it is getting

more "complicated" (time consuming) if large amount of >8 bit types and storage classes are involved...

 

Except for this limitation, SourceBoost has the right price and performance between SDCC and HiTech's PRO version.

Sorry to hear it's a deal breaker for you. Hopefully you will stick around these forums and use the limited free version for a while as we are all expecting good things of V7.

BTW.:

For arrays <256 bytes nothing would change. The original, limited implementation could still be used...

I beg to differ. It *does* need to be handled differently for 'rom far <type>*'

At the moment one can only index into a 'rom char <var>[]' array and pointer arithmetic is ILLEGAL. 'rom char*' is just an alias for the same implementation. The choice of 8 bit offset addressing for this with an separate table identifier is probably an artefact of SourceBoost's early choice to use function identifiers rather than true function pointers which is not unreasonable on a processor as limited as the older PIC16 range.

 

My vision for 'rom far <type>*' is that it could be used for read access to *ANY* table anywhere in Rom. The fly in the ointment is any errors could become very hard to trace as if it points at an instruction that is NOT a RETLW, the next read from it will jump to 'La-La land'. I would prefer to keep something this dangerous explicitly separated from the current array support and also to minimise the risk of breaking any existing code.

 

P.S Think of me as you will but I only answer to 'Ian' or other contractions of my full name.

Share this post


Link to post
Share on other sites

Okay, I seem to be lacking some understanding here. Perhaps someone with a bit more experience than I can help.

 

If I fill my ROM space with data using #pragma data (which I have done), then how can I access the data stored?

 

For example, as a test I wrote the following:

 

#pragma data 0x006 0x80 // This is just some sample data placed at address 0x006 to see if I can access it later on

 

 

and then, in my program, I tried to access the data using a pointer to 0x006. However, the data I get back is 0x00, not 0x80.

 

Clearly I am doing something wrong, but I cannot see what it is.

 

Is anyone able to steer me in the right direction?

 

Thanks,

 

Brian

Share this post


Link to post
Share on other sites

Ah, it has just occurred to me that when I try to access address 0x0006, I am probably reading PortB in data memory rather than the information I've manually placed in program memory.

 

So I am not addressing the memory correctly. Question is, how *do* I do this correctly?

 

Okay, time to study some datasheets.

 

Brian

Edited by Thermal Runaway

Share this post


Link to post
Share on other sites

Hi Brian,

 

BoostC may not be all that good yet at handling rom pointers. (V7.0 Yepee ;) )

 

You could try something like this for now.

#define MYROMDATA	0x0200
#pragma DATA MYROMDATA,1,2,3,4,5,6,7,8,9

rom char *ptr;
char x = 4;
ptr = MYROMDATA;
fred = ptr[x];

Access the rom data like an array rather than use a pointer. Doesn't look like good C but may be all you have for the time being unless you go the assembler route. This will use the __rom_get call to get the data.

 

Cheers

 

Reynard

Share this post


Link to post
Share on other sites
So I am not addressing the memory correctly. Question is, how *do* I do this correctly?

 

Try this UNTESTED code, which is a direct port of EXAMPLE 3-3: FLASH PROGRAM READ from the PIC16F88 datasheet:

#include <system.h>

unsigned int FlashRead(unsigned int ROMaddr)
{
unsigned int temp;
temp=ROMaddr;
asm {
movf _temp+1, W
movwf _eeadrh 	// MS Byte of Program Address to read
movf _temp, W
movwf _eeadr 	// LS Byte of Program Address to read
bsf _eecon1, EEPGD // Point to PROGRAM memory
// ***WARNING*** this code will NOT simulate nor single step with ICD	
bsf _eecon1, RD // EE Read 
nop 		// Any instructions  here are ignored as
nop 		// program memory is read in second cycle after bsf _eecon1, RD
// ***END WARNING***	
movf _eedata, W	// low byte
movwf _temp;
movf _eedath, W // high byte
movwf _temp+1;
}
return(temp);
}

#pragma DATA 0x100, "Testing",0x0d, 0x0a, 0

void main()
{
unsigned int addr=0x100;
trisb=0;
while (portb=FlashRead(addr++)); //output the string
//All done
while(1); //halt
}

I have compiled it for a PIC16F88 and verified that the assembler in the .lst file matches the example in the data sheet, but no simulator I have access to supports ROM reads and I haven't got a LCD or serial port wired up to my breadboard this weekend.

 

What its supposed to do: Read a null teminated string from ROM and write it one character at a time to PortB. I've left out all the configuration though as main() is only an example of how to call FlashRead().

 

You will have to test it with a debugger. Don't try to single-step or set breakpoints on the three instructions between the ***WARNING*** comments.

 

If it works or nearly works it can be cleaned up a lot. Only the core instructions between the warnings need to be in asm, the rest could be rewritten in C.

 

IanM

Share this post


Link to post
Share on other sites

Reynard,

 

I've just read your post as I was about to shut down my PC and go to bed, and I felt that I couldn't go to sleep unless I'd tried your suggestion!

 

I've had a quick try and it definitely doesn't seem to work, at least not using the MPLAB simulator anyway. If I make the pointer point to various places (ptr[1], ptr[2], ptr[3] etc) and make a variable 'test' equal to the location that the pointer is pointing to each time, then I get repeatable results (i.e. the same data each time I try it), but the data I get is not the data I have stored at that location in program memory.

 

It is occurring to me now that perhaps this is a problem with the simulator, and not with the code I've written. I'll try and use it to send a character to my GLCD tomorrow and see if it works in practice or not. If it doesn't work I should also be able to work out if the practical results agree with the simulator by looking at the pixels that have been enabled on the screen.

 

But it's way too late for that this evening so it's a job for tomorrow. Thanks for your response.

 

Ian: I'll also give your suggestion some thought tomorrow as well, thanks again for your input.

 

Goodnight!

 

Brian.

 

 

Hi Brian,

 

BoostC may not be all that good yet at handling rom pointers. (V7.0 Yepee ;) )

 

You could try something like this for now.

#define MYROMDATA	0x0200
#pragma DATA MYROMDATA,1,2,3,4,5,6,7,8,9

rom char *ptr;
char x = 4;
ptr = MYROMDATA;
fred = ptr[x];

Access the rom data like an array rather than use a pointer. Doesn't look like good C but may be all you have for the time being unless you go the assembler route. This will use the __rom_get call to get the data.

 

Cheers

 

Reynard

Share this post


Link to post
Share on other sites

Hi Brian,

 

I tested it using the SourceBoost IDE simulator and PIC18F2520. the variable fred came out with the vaue of 5.

 

Maybe it depends on the PIC type. I got a warning when assigning a constant to a rom pointer and the compiler produces some code to assign the value to the pointer but 'ptr' is never used in fetching the data.

 

It was worth a shot if nothing else. If you look at the assembler code produced by my example you can see how BoostC gets the data using the table read method, assuming a PIC that support it.

 

Cheers

 

Reynard

Share this post


Link to post
Share on other sites
Are you using a PIC that can reprogram its own flash? If you are, then you can read any ROM address directly *and* fetch arbitrary data which would bypass the ROM array/table size limits.

 

Hmmmm. Why is it relevant if my PIC can reprogram its own flash?

 

I've just checked Microchip's website and in the spec table for their devices they have a column called "self write". Is this the feature you are referring to? If so, then no my PIC does not have that capability. As it happens I'm going to have to upgrade my target device anyway due to a different limitation, so this might be an opportunity to replace it with a device that can reprogram its own flash.

 

But, I'm still left confused as to why this has any relevance to reading ROM memory and fetching arbitrary data. Please enlighten me!

 

Thanks very much,

 

Brian

Share this post


Link to post
Share on other sites
Hmmmm. Why is it relevant if my PIC can reprogram its own flash?

 

I've just checked Microchip's website and in the spec table for their devices they have a column called "self write". Is this the feature you are referring to?

*YES*

If so, then no my PIC does not have that capability. As it happens I'm going to have to upgrade my target device anyway due to a different limitation, so this might be an opportunity to replace it with a device that can reprogram its own flash.

Other benefits usually include the ability to use a Microchip (or clone) ICD 2, ICD 3, PICkit 2 or PICkit 3 programmer/debuggers to test your code step by step in your actual application circuit without having to buy any extra expensive parts like debug versions of the chip or special ICE pods. I recommend the PICkit 2 as although it is being superseded by the PICkit 3 it still offers good value for money and the software for it is far more developed. Give the PICkit 3 another six months or a year and my advice may change, but at the moment the PICkit 2 appears to be better value and technically more reliable than its successor. The ICD range have a muppet proof warranty but I do not have personal experiance of them. You do need to reserve /MCLR and the PGC and PGD lines (which may be called ICSPCLK and ICSPDAT and are usually on RB6 and RB7) for the debugger but the benefit is worth it. The debugger only runs under MPLAB. Other non self-write chips can be debugged but only if you buy expensive special debug versions of the chip mounted on an adaptor board.

But, I'm still left confused as to why this has any relevance to reading ROM memory and fetching arbitrary data. Please enlighten me!

Part of the self-write functionality is self-read as if you are writing a bootloader, you need to be able to verify the data has been stored correctly.

 

Using self-write itself is quite complicated with several pit-falls for the unwary, but the self-read is quite easy to use. It is almost identical to the code required to read the data EEPROM. I posted a direct conversion to BoostC asm of the Microchip MPASM code in the PIC16F88 datasheet. See post #25 above and see post #18 for the extract from the data sheet it was based on.

 

As it reads any address in ROM, you have the ability to read as much data as you wish to without the limitations of RETLW tables as implemented by the current BoostC rom char *variable[] arrays. RETLW is nasty at best, because it works by a computed jump into the table and the RETLW literally stuffs the 8 bit number embedded in the opcode into the W register then pops the return address off the stack and returns to it. This means that if you try to read past the end of a RETLW table you will get a mystery crash as you are jumping into random code, not just incorrect data returned. The FlashRead() code will return bad data if you read a location you didn't fill with a #pragma DATA but, assuming I have converted it from MPASM correctly, should never crash.

 

The RETLW problem is also half of why Reynard's suggestion to use the BoostC v6.xx rom array element syntax to read back values stored by #pragma DATA can never work. The data table doesn't conain the required RETLW opcodes. The *OTHER* reason it cannot work, is Boost C ENUMERATES pointers to ROM arrays like it does to functions. Neither the address of a ROM array name or of a function name gives you their actual ophysical address, It is just an index into a table that says where to find them. #pragma DATA cannot add the essential data to this indirection table. I would not even care to speculate where the data read was coming from and I suspect the stack was also being corrupted.

 

The ROM read routines for the other self-write capable Flash PIC16 series chips are very similar but there aren't many of them in current production in DIP (I dont keep track of SMD only stuff). Just the PIC16F88 and the PIC16F88x series. The PIC16F87x series introduced the feature and you should still be able to get PIC16F87xA chips. It is also available across most of the current PIC18 range, but there are some subtle changes to the addressing which would prevent the above code working without modification.

 

Remember, neither the Sourceboost or MPLAB simulators handle FLASH reads and writes yet so you have to use actual hardware to test your code. I also caution against trying to single step the critical code that actually reads the ROM. You can set a breakpoint safely before or after it with PICKit 2 but not in it.

 

Ian

Edited by IanM

Share this post


Link to post
Share on other sites

Okay Ian, thanks very much for the information - very helpful indeed. As it happens I already have a PicKit 2 and it will be very nice to be able to use it for hardware debugging. I think you're right; it is worth sacrificing a couple of I/O pins for this facility.

 

I've tried Reynard's suggestions but I was not able to get his ideas to work on my specific device. I think he's right - it probably is target device specific, so it's working on his but not on mine.

Having failed to obtain encouraging results with the simulator I did decide to try it in practice. It resulted in an endless loop of crashes. Curiously, the device has refused to power up at all since so I think it has been destroyed?! The GLCD that it is connected to has on board power supplies and is sensitive to their startup parameters. I take care of all this in some initialisation code that I wrote, but perhaps the crash loops have sent it haywire. Now that I think about it, perhaps it's the GLCD that's gone faulty, not the PIC itself, because I was basing my previous suggestion that the PIC has gone faulty solely on there being no output on the display anymore.

 

Anyway, as I said I need to upgrade the device regardless because of a RAM limitation (I'm implementing an SD card and didn't realise they must be read in 512 byte complete sectors) so this will be an opportunity to experiment with a target device that has the self-write functionality. Hopefully I can then get this working, and it'll also be interesting to experiment with the hardware debugger functionality.

 

Thanks again for the advice everyone - very much appreciated.

 

Brian.

Share this post


Link to post
Share on other sites

Your content will need to be approved by a moderator

Guest
You are commenting as a guest. If you have an account, please sign in.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×