Jump to content

ASM newbie requesting help / MOVWF vs. MOVFF and current Bank


This N°9
 Share

Recommended Posts

I'am new to the ASM world, and my aim is to develop a module for the Ramtron FM24C512 ferroelectric nonvolatile memory devices. See this thread: http://www.midibox.org/forum/index.php/topic,12641.0.html

To get a feeling of how the PIC ASM is used in modules and MIOS, I had a look at TK's sdcard driver, which I'll need also for my seq project. Studied PIC docs and gputils doc.

I also found the mod_skel in the repository, there is a template for C a function wrapper:

; _mod_skel_function	; example shows 
; 						; a char (1 byte, stared in W thanks to __wparam), 
; 						; an int (2 bytes)
;						; EG: void mod_skel_function(unsigned char myvar, signed int myint);
; 	movwf	_mod_skel_var		; get first byte of arguments from W
; 
; 	movff	FSR0L, FSR2L		; get other arguments from stack
; 	movff	PREINC2, _yourint+0
; 	movff	PREINC2, _yourint+1
; 	rgoto	mod_skel_function
In this template movwf is used to move the first function param byte from WREG to _mod_skel_var. But the C-wrapper of the sdcard module uses
movff	 WREG, _sdcard_address+0

to move the WREG. movff is using absolute 12-bit memory addresses, but movwf uses a 8-bit file-register address and the "a" flag (common block/ BSR bank). How can I be sure that the right bank is selected (or _mod_skel_var is in the common block) when moving WREG to _mod_skel_var with movwf ? Or should I use movff instead, like seen in sdcard.asm ?

and.. do 2-word-instructions execute in one cycle or is the core just able to load one word at once? I know that calls and branches / gotos take two cycles, but I'am not sure about 2-word instructions.

thanks a lot for your help

Link to comment
Share on other sites

How can I be sure that the right bank is selected (or _mod_skel_var is in the common block) when moving WREG to _mod_skel_var with movwf ? Or should I use movff instead, like seen in sdcard.asm ?

The mod_skel example is provided by StrydOne, so I can only speak for my SD card driver: In order to allow highest flexibility, I used "movff" here, so that it doesn't matter if _sdcard_address is located in the ACCESS bank or not. I could also set the BSR before doing an "movwf", but prefered movff here (balancing question, mostly subjective and decided within 2 seconds, so please don't ask which method is better ;)

In general, using the ACCESS bank speeds up the code, but it also reduces available directly accessible memory (e.g. for temporal variables) - therefore it's expensive and should only be used if it really makes sense.

and.. do 2-word-instructions execute in one cycle

no, but if you would have to set the BSR before transfering the the value into the register, it would result into 2*16 bit/2 cycles as well. In addition, it would destroy the current content of BSR, and it would lead to two code lines instead of one.

If destroying the BSR is no issue, I usually decide to use either SET_BSR/movwf ...BANKED or movff depending on the lunar calendar ;)

If you are asking for the best solution: allow to locate your variables into the BANKED area so long the functions are taking more than 10 uS execution time (so that one or more additional cycles don't really matter). If more than one BANKED register is accessed and BSR doesn't need to be restored, change the BSR instead of using movff

Best Regards, Thorsten.

Link to comment
Share on other sites

Gday mate,

I kinda threw that skeleton together, so there would be an example of how the modules interact with the app. There's a matching template application that goes with the module template, to help to demonstrate the way that variables and such can be passed to or from the module.

Anything (functions, variables, defines, etc) with the name mod_skel* is just there as a kind of placeholder, so that you can find where you might put your code for a similar use-case.

Basically, when you are done writing your driver, do a search for mod_skel in those files, and you should not have any remaining.

But I would not say that it is a great example of how to code in ASM ;) In that case, movwf works fine, but it won't always be that way. As you noticed, movwf requires you to set the correct bank, before you use that instruction. Ensuring that is always the case, is up to you (doh!).

The upside, is that movwf is a single-cycle instruction. So, if you need to do several sequential movwf instructions, moving data to the same bank, you can save a few cycles by using movwf.

However if ensuring the correct bank is used, is difficult, or if you are copying data to several different banks, then movff would be more suitable.

In the instruction set summary table in the datasheet, there is a column showing how many cycles each instruction takes to execute. In ther, you'll notice that all the two-word instructions, also take two cycles to execute. I'm assuming that the internals of the PIC only allow for one instruction word to be loaded per-cycle (which makes sense I guess)

Hope that helps!

Link to comment
Share on other sites

wow, this is quick "service". thanks a lot.

allow to locate your variables into the BANKED area so long the functions are taking more than 10 uS execution time

how do I instruct the assembler to locate data in the common block / banked ? I think I'll not really need that, but just to see things a bit clearer

something else that confused me a bit was this TRIS/Tristate issue. I did some research about Tristate logic, but in the most descriptions this is not linked to I/O ability. But as I understood until now, with the PIC a pin set to TRIS 1 (high Z) can act as high-z input then, and some of them can be driven as open-drain-outputs by setting data bit to 0 and driving TRISXX for data (?).

I had also a look in the bankstick code (as my module will use the same port and is closely related to bankstick), and there I found this

;; if RA4 is not an open drain driver, set this to 0 (e.g. PIC18F4620)
;; in this version, we always disable it to get an identical behaviour
#define RA4_IS_OPEN_DRAIN 0

should I take this into account when developping the module or is this just a "dead branch" ?

my questions may be a bit fuzzy, but every info is helpful for me

Link to comment
Share on other sites

how do I instruct the assembler to locate data in the common block / banked ?

the linker will locate udata sections into BANKED area, and udata_acs sections into ACCESS area

Btw.: this seems to be a bug in the mod_skel example, as it locates variables into the udata section w/o taken care of the BSR

something else that confused me a bit was this TRIS/Tristate issue.

Short answer: In earlier derivatives like PIC16xx and PIC18F452, RA4 was only available as open drain pin due to compatibilty reasons. All other pins are working in Push Pull Mode

Please ask Microchip support for the reasons ;)

But this isn't an issue in your particular case (therefore just ignore the comment in the BankStick driver...)

You don't use RA4, and if you would need open drain capabilities, you could control this via SW by setting LATx.y to 0 and toggling TRISx.y instead of LATx.y

Best Regards, Thorsten.

Link to comment
Share on other sites

Btw.: this seems to be a bug in the mod_skel example, as it locates variables into the udata section w/o taken care of the BSR

Agreed...

;; --------------------------------------------------------------------------
;;  FUNCTION: mod_skel 
;;  DESCRIPTION: mod_skel. does something stupid. Put your code in here instead.
;; 

Maybe it would be a good idea to put the BANKSELs in there anyway... avoids this confusion...

OK it's on my list...

Link to comment
Share on other sites

thanks, this is very usefull info for me. I'll correct the "template bug" in the mod_skel by replacing movwf by moveff.

Short answer: In earlier derivatives like PIC16xx and PIC18F452, RA4 was only available as open drain pin

PIC18F452 is exactly the one I'am using.. / have spare ones. I had a look at smashTV's store and see that he sells now the PIC18F4685.. When I did my last order, it was still the 452. In the datasheet I see the new one has double RAM space and more code space available...

I have/want to use RA4  because I want to use the same pins as the bankstick/iic would, so RA4 is my dataline. This means also that I need to switch it input<->output.

If the pin is only available as open-drain, does this mean it can be used as input when it is in high-z-state? This looks logic to me if I look at the code in mios_iic.inc:

	
bsf	MIOS_IIC_LAT_SDA, MIOS_IIC_PIN_SDA	; SDA = input
#if RA4_IS_OPEN_DRAIN == 0
	bsf	MIOS_IIC_TRIS_SDA, MIOS_IIC_PIN_SDA	; SDA = input
#endif

I suppose you just do both to match newer/older derivates?

and if I'am already asking a lot of questions: what stands LAT for?

Is there a good information resource on the web or so where I can get an overview of all these principals?

thanks a lot

Link to comment
Share on other sites

thanks, this is very usefull info for me. I'll correct the "template bug" in the mod_skel by replacing movwf by moveff.

Oh yeh, that's easier than banksels :) Thanks!

I had a look at smashTV's store and see that he sells now the PIC18F4685.. When I did my last order, it was still the 452. In the datasheet I see the new one has double RAM space and more code space available...

That's right. Essentially my rule of thumb is: 4620 (from smash, to avoid the EUSART bug) or if I need CAN, 4685. That's because some of the extra RAM in the 4685 is reserved for the CAN peripheral, so the 4620 does have a bit more space than it seems.

I have/want to use RA4  because I want to use the same pins as the bankstick/iic would

If it's not communicating using IIC, I think this would be a shame... Especially with the theoretical ability to handle lots of fast data, IIC_MIDI modules seem to be in order, so I'd try to keep those free. Personally, I'd be aiming for J10, unless you plan on using this with a SID; or maybe J7, if you don't need more than 8 AINs. Alternatively, you can share pins between your buss (what buss is it anyway?)and others. Usually you only need one extra line, so you can use the touchsensor pin or something else that's rarely used.

If the pin is only available as open-drain, does this mean it can be used as input when it is in high-z-state?

Yep they'll all act as an input in hi-z mode (TRISx 1)

BTW Isn't it convenient that it's 1-nput and 0-utput :)

what stands LAT for?

Latch. Check out this post: How pins work It's got a lot of information that will be boring to you, but also has a few specifics that might help...

Is there a good information resource on the web or so where I can get an overview of all these principals?

PICList.com is my favourite jump-off for all things PIC.

Link to comment
Share on other sites

happy new year to the australian guys!  :D

If it's not communicating using IIC, I think this would be a shame...

well, it is... Communication protocoll is IIC. It works very similar to the bankstick, but the protocoll is a bit different because it's not a eeprom, but a FRAM, single bytes can be written / read at same speed (full clock speed max. 1MHz), no need to wait for the write cycle to finish  :)

The sharing *could* be a problem, because my aim is not to start/perform/stop a whole read/write sequence within one single function.

the advantage of this: I can start a read (setting address etc.), after a ACK the device will increment the internal address latch to the next byte, so subsequent reads of single bytes are possible (or writes). For me this is very important, because this enables me to "stream" data very fast, say read a byte or more, check what it is, decide if I need to read more, if not, I perform a stop condition.

I could write some low-level-funtions that do this, like:

unsigned char MIOS_FRAM_ReadStart(unsigned char devicenr, unsigned int addr);

unsigned char MIOS_FRAM_WriteStart(unsigned char devicenr, unsigned int addr);

unsigned char MIOS_FRAM_ReadByte(void);

unsigned char MIOS_FRAM_WriteByte(unsigned char)

unsigned char MIOS_FRAM_ReadBuf(unsigned char * buf, unsigned int byte_count)

unsigned char MIOS_FRAM_WriteBuf(unsigned char * buf, unsigned int byte_count);

unsigned char MIOS_FRAM_ReadWriteStop(void);

These ones would be "unsave" to share with other IIC devices, if a read/write sequence will stretch over several main-loops. If the whole operation is done in one tick-call, the IIC bus should be free on the next MIOS-mainloop.

Or are IIC operations also done in ISR/Timer callbacks?

There could be also high-level-functions which do a start/rw/stop sequence in one call, like with the BankStick-Functions.

More reasons why I would like to use PORTA/J4 is, that the device will be a direct alternative to the banksticks, and I plan to use J10/PORTC to multiplex up to 16 of the FRAM devices (4 can be addressed by pins, 4 arrays would be multiplexed with a 4channel analog dual multiplexer).

However, the device is not very DIY friendly (only available in SOIC package so far). I just use it because I need fast stream ability from/to a big data space.

Maybe I could use the mios_iic module, but this would mean some more overhead/more calls etcetc. If I implement all in one single module, I can make it very tight. But I'll study the mios_iic module anyway, maybe there are good reasons to use already existing iic functions.

Can I drive the IIC with 1MHZ with the mios_iic ?

Latch. Check out this post: How pins work It's got a lot of information that will be boring to you, but also has a few specifics that might help...

Thanks for link, I think stuff like this is worth to flow into the wiki? I found some other page on the WWW that explain the issue quite clear:

http://www.fernando-heitor.de/component/option,com_openwiki/Itemid,123/id,mikrocontroller:einfuehrung/

If I understand right, the general rule is: to set pin states, use LATxx, to read them use PORTxx. To set in/out configuration, use TRISxx while TRISxx=1 means "input" and TRIXxx=0 means "output" (for the PIC)

Link to comment
Share on other sites

Communication protocoll is IIC.

In that case it should be possible to write your driver in a way that allows other devices to share the buss :)

The sharing *could* be a problem, because my aim is not to start/perform/stop a whole read/write sequence within one single function.

Maybe just set a flag to prevent other IIC functions from being called when your device is locking the buss.

Or are IIC operations also done in ISR/Timer callbacks?

Nah, they are blocking though.

More reasons why I would like to use PORTA/J4 is, that the device will be a direct alternative to the banksticks,

It's not an alternative to IIC MIDI or GLCDs though ;) Point is, if you exclude all of the existing IIC devices, you aren't just excluding banksticks... Any way I don't think that should be an issue.

Maybe I could use the mios_iic module, but this would mean some more overhead/more calls etcetc. If I implement all in one single module, I can make it very tight.

It comes down to a matter of the desired implementation really. You'll only be duplicating existing code if you make your own IIC handler, and the calling overheads are up to you, so personally I would use the existing functions. If you're really worried about losing a couple of cycles now and then to call the mios_iic functions, you can write your own, and you'll need multiple copies of certain things, so you'll be duplicating the code that you duplicated already. It will be marginally faster, but the codespace used will be much larger. Honestly I think that the bigger bottleneck is not calling overheads, but simply the speed of the buss (even at 1mhz) , and the tradeoff would not be worth it.

Can I drive the IIC with 1MHZ with the mios_iic ?

It'll go as fast as the PIC will :)

Thanks for link, I think stuff like this is worth to flow into the wiki?

I'm never sure where to put stuff like that :/ I often take a few minutes out to explain stuff like that.

I would like to have it on the wiki though - even when I knew it was here, it took me three searches to find it ....

I found some other page on the WWW that explain the issue quite clear:

http://www.fernando-heitor.de/component/option,com_openwiki/Itemid,123/id,mikrocontroller:einfuehrung/

Ich bin ein Germanduden. :D

If I understand right, the general rule is: to set pin states, use LATxx, to read them use PORTxx. To set in/out configuration, use TRISxx while TRISxx=1 means "input" and TRIXxx=0 means "output" (for the PIC)

You got it!

Link to comment
Share on other sites

happy new year to everybody!

In that case it should be possible to write your driver in a way that allows other devices to share the buss

This would be nice. The FRAM is using the same slave id (b1010) and almost the same control byte (first byte after "start" condition):

FM24C512(Ramtron FRAM):    [1][0][1][0][A2][A1][A15][R/W]

24LC512(Microchip EEPROM): [1][0][1][0][A2][A1][A0][R/W]

A0 - A1 are device select bits. FM24C512 has only 2 device select pins, A15 is the address MSB, the FM24C512 addressspace is parted into two sections which can not be writte or read continously.

banksticks/fram could be even mixed, each FRAM would take two addresses of 8, each bankstick just one.

Maybe just set a flag to prevent other IIC functions from being called when your device is locking the buss.

Nah, they are blocking though.

So the "cheap" solution would be to just have the multiple-call-communication as "expert mode", where people are adviced not to call other IIC-related functions/devices while having a open communication to FRAM. This would be wise anyway, because it's better for the coder to know when the IIC port is blocked, and in most of the cases the code will not do anything else during a read/write session.

For the "save" read/write I just would supply some functions that wrap the calls to make a port-blocking function.

It comes down to a matter of the desired implementation really. You'll only be duplicating existing code if you make your own IIC handler, and the calling overheads are up to you, so personally I would use the existing functions. If you're really worried about losing a couple of cycles now and then to call the mios_iic functions, you can write your own, and you'll need multiple copies of certain things, so you'll be duplicating the code that you duplicated already. It will be marginally faster, but the codespace used will be much larger. Honestly I think that the bigger bottleneck is not calling overheads, but simply the speed of the buss (even at 1mhz) , and the tradeoff would not be worth it.

Thats true, the IIC stuff is already implemented, and at a short glance in the code, I see the same timings as I read in the FM24C512 datasheet, like 600ns to wait for ACK beeing set etc. So using mios_iic will be the plan.

This also simplifies the way for sharing the port between devices/protocols in the future, if some changes on iic-level should be done.

Ich bin ein Germanduden.

sorry about that. But the schematics for the two output types (open drain/push-pull) for the PIC are labeled in english. I attach them here. One is the i/o pin schematic of the pic16, the other pic 18(with readable Latch). Looking at them made the whole thing for my a lot clearer.

thanks for all the info, I think I'm ready now to start with the FRAM module.  :)

4198_io_pin_pic16_gifc7a314c577e16d8393f

4200_io_pin_pic18_gifa04809c4eb1392b1e80

4198_io_pin_pic16_gifc7a314c577e16d8393f

4200_io_pin_pic18_gifa04809c4eb1392b1e80

Link to comment
Share on other sites

there's something to mention, this could be important:

I looked at the timings of the mios_iic, and it looks like it's target clock frequency is 400kHZ instead of 1MHZ (PIC@10MIPS). A single bit read / write loop takes 23 / 24 cycles, an ACK even 33 cycles. If I calculate just the single bit r/w, I get 416 - 434 KHz.

This more that halfs the speed that the Ramtron FRAM supports (min.  clock high 0.4uS; min clock low 0.6 uS; data out valid max. 0.55uS; data in setup min. 100ns.;data hold in/out 0nS).

One option would be to have a flag that sets the IIC to 1MHZ fast mode. This could also be a #define, with disadvantage not to have control @ runtime. Doing it by a define would allow me to not have the iic-code double, the code size / mem usage of MIOS would not rise (even fall) with the #define fast_mode 1 option. The only thing I would have to do, is to modify some delays if  #define fast_mode 1 is set.

If you (TK and others) like the modified version when it's finished and tested, it could go to repository definitively.

Link to comment
Share on other sites

This will result into the problem, that the IIC specs will be violated for all other IIC slaves connected to the bus.

Not only that it wouldn't be possible to access them anymore if the switch is enabled, it could also result into corrupted data transfers if your device is accessed faster than 400 kHz while other slaves should be in passive state (e.g., because they cannot read their address fast enough, and could unintentionally assume, that they have to drive the bus)

Accordingly, I strongly recomment you to provide separate functions at module basis, and to use the IIC pins only optionally. In your specific case it could make sense, for other applications it's a killer, as other IIC slaves (especially MBHP_IIC_MIDI) cannot be used in parallel.

Best Regards, Thorsten.

Link to comment
Share on other sites

Ok, I see the point. I see different variants:

- use the mios_iic with 400kHz. This would make the port shareable with other iic-devices, J10 would stay free for optional multiplexing.

advantage: shareability, no additional port will be used, smaller code (iic already part of mios)

disadvantage: slower transfer rates

- use some other (configurable) port. default would be something else than iic (use iic at your own risk).

advantage: full clock speed. 8 banksticks could be used in parallel with 16 FRAM (with multiplexing) theoretically.

disadvantage: more code, one more port will be used.

The speed loss with the first option is not so tragic, because the big advantage of the FRAM is write speed (in opposite to the banksticks), and higher endurance (1 billion r/w cycles).

I can't decide this right now, maybe you have some opinion to that. For my own (sequencer) needs, both options would work well.

Link to comment
Share on other sites

I will implement the module like this:

- I implement iic functions in my module that run @ 1MHZ

- Standard port will be RC (0/1). This can be reconfigured with defines.

- a #define selects if the mios_iic functions should be used. In this case, the 1MHZ iic-functions will not be included.

- for both options, there will be a (optional) 4x multiplex mechanism. Default port will be J10 (RC4/RC5). This also can be changed.

this way, most flexibility is guaranteed. For devices that neither use MF nor AIN, the faster solution can be chosen.

Link to comment
Share on other sites

  • 3 weeks later...

Oh thats nice! MIOS32 will support 1MHz IIC natively. As soon as I have the STM32 (and checkd it out a bit), I'll try to port the FRAM module.

I also made some benchmark tests with the FRAM:

subsequent block writes/reads, 16KBytes in one chunk, repeated 16 times (4xFM24C512) (address just sent once at the begining):

fast mode:

write 256KBytes: ca. 2.9Sec

read 256KBytes: ca. 2.7Sec

This is a bit faster (if I calculated right) than your benchmark, but I suppose it's because I only (need to) send the device- and memory-address once for 16KBytes transfer.

I really could go to the edge of the timing maximum ratings and noticed no problems so far with a ca. 15cm long cable. The big advantages of the FRAM is the write-speed and the endurance (1 billion r/w cycles). Drawbacks are the price (ca. 10euro for a FM24C512), and they are only available as SMD.

Current state: mios_iic / fram_iic@1MHZ successfully tested over J4. Builded a board stuffed with a 4-channel analog multiplexer and 16xFM24C256 -> 1MB. The default setup (for PIC) will be: RC4/RC5 SCL/SDA; RD5/RD6 multiplexing. Everything can connect with one single ribbon cable.

I'am currently trying to run tests with the multiplexed version, but I have some problems. Maybe I have to add pull-up's, or it's something else.. I'll find out.

Once things will work completly, I'll create a wiki-page for the module and give some more info there.

by the way: aatis.de sells these little SMD adapter for a very modest price. I don't know if they ship only to europe or also overseas:

http://www.bausatz.aatis.de/SO2DIP_SMD-Adapter/body_so2dip_smd-adapter.html

EDIT: just read the spec again, output of the device is open-drain. So I add pull-ups. This also has the advantage, that if the wiring is incorrect, NAK will be read by default. The good thing about this: because I missed the pull-ups, I found a bug in the code (high-level read returns zero if the last byte read was a 0x00, that's defined as error-response)  :D

4375_fram_multiplexed_jpg3ff9fc3cc7c09bf

4375_fram_multiplexed_jpg3ff9fc3cc7c09bf

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
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...
 Share

×
×
  • Create New...