• AVR Freaks

Helpful ReplyHot!Assembly in PIC32

Page: 1234 > Showing page 1 of 4
Author
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
2020/01/30 02:54:48 (permalink)
5 (1)

Assembly in PIC32

Hello,
I am using PIC32MK1024GPD064 and MPLAB x IDE v5.30. 
I need to write an assembly routine to output bits in diferent PORTS.
For example, this is part of my code in C:
 
LATFbits.LATF1  = 0;
LATBbits.LATB10 = 0;
LATGbits.LATG6  = 1;
LATAbits.LATA0  = 1;

I'm struggling to find information on how to do this in assembly, and hoping someone can point me in the right direction. I have little to none experience in assembly and can't seem to find any information about it. 
 
PS: If you're wondering, I need to speed up the code because I'm outputing to a DAC, thats why I'm trying it in assembly. 
 
 
#1
ric
Super Member
  • Total Posts : 27716
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Assembly in PIC32 2020/01/30 03:19:59 (permalink)
4.75 (4)
Correctly written C will be just as fast as assembly for this.
Try:

LATFCLR = 0b0000000000000010;    // LATF1  = 0;
LATBCLR = 0b0000010000000000;    // LATB10 = 0;
LATGSET = 0b0000000001000000;    // LATG6  = 1;
LATASET = 0b0000000000000001;    // LATA0  = 1;

 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#2
JPortici
Super Member
  • Total Posts : 1094
  • Reward points : 0
  • Joined: 2012/11/17 06:27:45
  • Location: Grappaland
  • Status: offline
Re: Assembly in PIC32 2020/01/30 03:52:12 (permalink)
4.67 (3)
Which, in human-readable form becomes
 
#include <xc.h>
 
LATFCLR = _LATF_LATF1_MASK;
//etc

#3
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 04:18:41 (permalink)
0
Thanks, that is really helpfull and it did speed up the code. 


I gave a simple example so I could know where to start, but what I really need is a little harder.
This is the code I wrote, the diferent bits are the ones connected to the DAC I'm using.

 
while(1) {
num = *(p + i);
CH4_LPFOn(); // Cycle time test!
if (num & (1 << 0)) LATFCLR = 0b0000000000000010; // if bit 0 is set //F1
else LATFSET = 0b0000000000000010;
if (num & (1 << 1)) LATBSET = 0b0000010000000000; // if bit 1 is set //B10
else LATBCLR = 0b0000010000000000;
if (num & (1 << 2)) LATBSET = 0b0000100000000000; // if bit 2 is set //B11
else LATBCLR = 0b0000100000000000;
if (num & (1 << 3)) LATBSET = 0b0001000000000000; // if bit 3 is set //B12
else LATBCLR = 0b0001000000000000;
if (num & (1 << 4)) LATBSET = 0b0010000000000000; //B13
else LATBCLR = 0b0010000000000000;
if (num & (1 << 5)) LATBSET = 0b0100000000000000; //B14
else LATBCLR = 0b0100000000000000;
if (num & (1 << 6)) LATBSET = 0b1000000000000000; //B15
else LATBCLR = 0b1000000000000000;
if (num & (1 << 7)) LATGSET = 0b0000000001000000; //G6
else LATGCLR = 0b0000000001000000;
if (num & (1 << 8)) LATGSET = 0b0000000010000000; //G7
else LATGCLR = 0b0000000010000000;
if (num & (1 << 9)) LATGSET = 0b0000000100000000; //G8
else LATGCLR = 0b0000000100000000;
if (num & (1 << 10)) LATGSET = 0b0000001000000000; //G9
else LATGCLR = 0b0000001000000000;
if (num & (1 << 11)) LATASET = 0b0001000000000000; //A12
else LATACLR = 0b0001000000000000;
if (num & (1 << 12)) LATASET = 0b0000100000000000; //A11
else LATACLR = 0b0000100000000000;
if (num & (1 << 13)) LATASET = 0b0000000000000001; //A0
else LATACLR = 0b0000000000000001;

i++;
if (i==25) i=0;
CH4_LPFOff(); // Cycle time test!
 
}
 

 
I'm generating a sin wave, with a lookup table with 25 points. Problem is that I need a faster code to achive higher frequencies, thats why I wondered if Assembly would be better to do this.
 
I can provide more information on my clock settings and the output frequency I need but at this point my question is, is it worth it to code in Assembly instead?
 
post edited by mjrodrigues - 2020/01/30 07:51:30
#4
ric
Super Member
  • Total Posts : 27716
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Assembly in PIC32 2020/01/30 04:26:23 (permalink)
5 (1)
A better arrangement of the pin connections could have saved you a lot of pain!
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#5
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 04:30:36 (permalink)
0
Indeed! Having all the 14bits in the same PORT would be much faster and solve my problem, but I'm trying to find some other solution that doesn't envolve printing a bunch of new boards Smile: Smile
#6
LdB_ECM
Super Member
  • Total Posts : 378
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 04:43:47 (permalink)
5 (1)
You can simplify PORTC and PORTG with bit logic because they are in right order and do them all at once.
 
For the clear port simply mask off the bits from NUM for the port using AND and shift to location.
For the set port mask off the bits from NUM invert mask bits using XOR AND shift to location
 
LATBCLR = (num & 0x007E) << 9);
LATBSET = ((num & 0x007E) ^ 0x7E) << 9);

LATGCLR = (num & 0x380) ;
LATGSET = ((num & 0x380) ^ 0x380);

 
That covers 10 of the bits
 
However if it speed you are after build the port table in memory which is 4 ports (A, B, G, F) and set and clear. So 8 x uint16_t for each step in waveform and then simply load the 8 values and store then to the ports. Costs RAM space but gives speed.
 
post edited by LdB_ECM - 2020/01/30 04:54:53
#7
Mysil
Super Member
  • Total Posts : 3671
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: Assembly in PIC32 2020/01/30 06:46:40 (permalink)
5 (1)
Hi,
About the original question about assembly code,
there are examples installed together with C compiler, somewhere like:
"C:\Program Files (x86)\Microchip\xc32\v2.30\examples\assembly_examples\ports_control"
 
But as mentioned above, coding output pin manipulation in assembly is not likely to be faster than what is suggested above.
 
    Mysil
post edited by Mysil - 2020/01/30 06:50:43
#8
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 07:25:33 (permalink)
0
Thank you, I spent hours trying to find something like this lol
#9
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 07:47:21 (permalink)
0
LdB_ECM
You can simplify PORTC and PORTG with bit logic because they are in right order and do them all at once.
 
For the clear port simply mask off the bits from NUM for the port using AND and shift to location.
For the set port mask off the bits from NUM invert mask bits using XOR AND shift to location
 
LATBCLR = (num & 0x007E) << 9);
LATBSET = ((num & 0x007E) ^ 0x7E) << 9);

LATGCLR = (num & 0x380) ;
LATGSET = ((num & 0x380) ^ 0x380);

 
That covers 10 of the bits
 
However if it speed you are after build the port table in memory which is 4 ports (A, B, G, F) and set and clear. So 8 x uint16_t for each step in waveform and then simply load the 8 values and store then to the ports. Costs RAM space but gives speed.
 



Honestly struggling to understand the XOR part. 
For PORTB for example, first it will clear bit 10:15. But in the second line, if num is (111111 & mask) ^ mask, won't that clear every bit to 0?

For the second part, I do need to make this routine as fast as I can, could you please elaborate a little bit more on how could I do what you mentioned? What do you mean build the port table in memory? 

I'm a newbie so bear with me and thank you again!
#10
andersm
Super Member
  • Total Posts : 2823
  • Reward points : 0
  • Joined: 2012/10/07 14:57:44
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 07:58:06 (permalink)
0
mjrodriguesFor the second part, I do need to make this routine as fast as I can, could you please elaborate a little bit more on how could I do what you mentioned? What do you mean build the port table in memory?

Eg. something like this:

uint32_t a_bits = (num & 0x3800) >> 11;
LATASET = aset_table[a_bits];
LATACLR = aclr_table[a_bits];
uint32_t b_bits = (num & 0x7e) >> 1;
LATBSET = bset_table[b_bits];
LATBCLR = bclr_table[b_bits];
uint32_t g_bits = (num & 0x780) >> 7;
LATGCLR = gset_table[g_bits];
LATGCLR = gclr_table[g_bits];

If there's nothing else connected to the ports, you could just write the whole latch register in one go. Since port F only has one bit, it's not really worth making a table for.
#11
LdB_ECM
Super Member
  • Total Posts : 378
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/30 08:57:52 (permalink)
0
mjrodrigues
Honestly struggling to understand the XOR part. 



XOR LOGIC
1 1 = 0
1 0 = 1
0 1 = 1
0 0 = 0
 
So if you take any number and XOR it with a mask anything with a 1 in the mask inverts and anything with a 0 stays the same (make the left hand most your mask bit and the 2nd is the bit from your data) and you should get it
 
So
0x1E ^ 0x3E will give 0x20 (binary 0b00011110 ^ 0b00111110 = 0b00100000)
0x2E ^ 0x3E will give 0x10 (binary 0b00101110 ^ 0b00111110 = 0b00010000)
0x00 ^ 0x3E will give 0x3E (binary 0b00000000 ^ 0b00111110 = 0b00111110)
 
As any bit that is a 1 for the CLEAR PORT you don't want to hit on the SET PORT
What you want is to simply flip the bits between the SET and CLEAR PORT.
#12
NorthGuy
Super Member
  • Total Posts : 6185
  • Reward points : 0
  • Joined: 2014/02/23 14:23:23
  • Location: Northern Canada
  • Status: offline
Re: Assembly in PIC32 2020/01/30 11:40:43 (permalink)
0
Thew easiest (and fastest) way is
 
LATAINV = (LATA ^ new_value) & mask;

post edited by NorthGuy - 2020/01/30 11:42:53
#13
ric
Super Member
  • Total Posts : 27716
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: offline
Re: Assembly in PIC32 2020/01/30 12:37:15 (permalink)
0
mjrodrigues
...
Honestly struggling to understand the XOR part. 
For PORTB for example, first it will clear bit 10:15. But in the second line, if num is (111111 & mask) ^ mask, won't that clear every bit to 0?

Bear in mind how the SET and CLR registers work.
They ONLY affect bits where there is a "1" in the value being written.
"0" bits have no effect on the destinaton port.
 
The XOR is required on the write to the CLR register because we need 1 bits in the positions we want to clear to 0, so it's necessary to invert all the bits being written.

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#14
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/31 03:07:29 (permalink)
0
I think I found out why I was confused, when I first posted my code, I had the instructions switched up (the LATxSET and LATxCLR) so LdB_ECM gave me the answer for the code I had...
 

LATBSET = ((num & 0x007E) << 9);
LATBCLR = (((num & 0x007E) ^ 0x7E) << 9);

This is what I actually want.

Lets say I want to output 13006 (11001011001110)
LATBSET = ((...1001110 & 1111110) << 9) = (1001110<<9) = 1001110000000000
LATBCLR = ((1001110 ^ 1111110)<< 9) = (0110000<<9) = 0110000000000000
 
Took me long enough grin: grin
#15
simong123
Lab Member No. 003
  • Total Posts : 1391
  • Reward points : 0
  • Joined: 2012/02/07 18:21:03
  • Location: Future Gadget Lab (UK Branch)
  • Status: offline
Re: Assembly in PIC32 2020/01/31 03:53:57 (permalink)
0
Try:
LATBINV=(LATB^(num<<9))&(0x007E<<9);
*Probably* faster (haven't tested), but at least shouldn't be slower.
#16
simong123
Lab Member No. 003
  • Total Posts : 1391
  • Reward points : 0
  • Joined: 2012/02/07 18:21:03
  • Location: Future Gadget Lab (UK Branch)
  • Status: offline
Re: Assembly in PIC32 2020/01/31 04:29:39 (permalink)
5 (2)
Considering the following code
volatile uint32_t num=0x0050;

int main(void)
{
    //SET&CLR
    {
        LATBSET = ((num & 0x007E) << 9);
        LATBCLR = (((num & 0x007E) ^ 0x7E) << 9);
    }

    //Using INV instead of SET and CLR
    {
        LATBINV=(LATB^(num<<9))&(0x007E<<9);
    }
}


The SET&CLR code produces the following at -O1
    .loc 1 108 0
    lw    $3,%gp_rel(num)($28)
    andi    $3,$3,0x7e
    sll    $3,$3,9
    lui    $2,%hi(LATBSET)
    sw    $3,%lo(LATBSET)($2)
    .loc 1 109 0
    lw    $3,%gp_rel(num)($28)
    nor    $3,$0,$3
    andi    $3,$3,0x7e
    sll    $3,$3,9
    lui    $2,%hi(LATBCLR)
    sw    $3,%lo(LATBCLR)($2)


The using xor and the INV register produces
    .loc 1 113 0
    lw    $3,%gp_rel(num)($28)
    lui    $2,%hi(LATB)
    lw    $2,%lo(LATB)($2)
    sll    $3,$3,9
    xor    $3,$3,$2
    andi    $3,$3,0xfc00
    lui    $2,%hi(LATBINV)
    sw    $3,%lo(LATBINV)($2)


So 3 instr. fewer.
 
This can be coded into a macro
#define _GETLAT(x)      LAT ## x
#define _GETLATINV(x)   LAT ## x ## INV
#define SETBITS(port,val,mask,shift) do{_GETLATINV(port) = (_GETLAT(port) ^ ((val)<<(shift))) & ((mask) << (shift));}while(0)
 
SETBITS(B,num,0x7E,9);

#17
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/31 05:00:59 (permalink)
0
simong123
Considering the following code
volatile uint32_t num=0x0050;

int main(void)
{
    //SET&CLR
    {
        LATBSET = ((num & 0x007E) << 9);
        LATBCLR = (((num & 0x007E) ^ 0x7E) << 9);
    }

    //Using INV instead of SET and CLR
    {
        LATBINV=(LATB^(num<<9))&(0x007E<<9);
    }
}


The SET&CLR code produces the following at -O1
    .loc 1 108 0
    lw    $3,%gp_rel(num)($28)
    andi    $3,$3,0x7e
    sll    $3,$3,9
    lui    $2,%hi(LATBSET)
    sw    $3,%lo(LATBSET)($2)
    .loc 1 109 0
    lw    $3,%gp_rel(num)($28)
    nor    $3,$0,$3
    andi    $3,$3,0x7e
    sll    $3,$3,9
    lui    $2,%hi(LATBCLR)
    sw    $3,%lo(LATBCLR)($2)


The using xor and the INV register produces
    .loc 1 113 0
    lw    $3,%gp_rel(num)($28)
    lui    $2,%hi(LATB)
    lw    $2,%lo(LATB)($2)
    sll    $3,$3,9
    xor    $3,$3,$2
    andi    $3,$3,0xfc00
    lui    $2,%hi(LATBINV)
    sw    $3,%lo(LATBINV)($2)


So 3 instr. fewer.
 
This can be coded into a macro
#define _GETLAT(x)      LAT ## x
#define _GETLATINV(x)   LAT ## x ## INV
#define SETBITS(port,val,mask,shift) do{_GETLATINV(port) = (_GETLAT(port) ^ ((val)<<(shift))) & ((mask) << (shift));}while(0)
 
 
 
SETBITS(B,num,0x7E,9);





Thank you for taking the time, and yes, it is indeed faster ! Will do the same for port G.
#18
mjr93
Starting Member
  • Total Posts : 38
  • Reward points : 0
  • Joined: 2019/12/20 03:53:34
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/31 08:37:34 (permalink)
0
Here are some results now:
With my inicial code:
 
https://ibb.co/b6YcC3s

And after:

https://ibb.co/ysKqBgd

Much better resolution now. I will work on the distortion, and still learning about how control the output frequency as I wish.
I am using an infinite loop to output the table values, so can someone help explain why after some cycles I see that "delay" visible in the after image?

PS: I'm running at 120Mhz clock, sample clock 1Mhz and 100 samples.
#19
andersm
Super Member
  • Total Posts : 2823
  • Reward points : 0
  • Joined: 2012/10/07 14:57:44
  • Location: 0
  • Status: offline
Re: Assembly in PIC32 2020/01/31 12:59:40 (permalink)
5 (1)
After all talk of not using assembly, it might be possible to do some poor man's SIMD, doing two ports in ten instructions (plus some non-recurring setup). I'm sure there's some real dumb mistakes in here, though.

setup:
lui $t0, %hi(0x3c0fc00)
ori $t0, $t0, %lo(0x3c0fc00)
lui $t1, %hi(PORTA)
loop:
lw $t2, %gp_rel(num)($gp)
sli $t3, $t2, 15
sli $t2, $t2, 9
or $t2, $t2, $t3
lhu $t3, %lo(LATB)($t1)
lwl $t3, %lo(LATG)+1($t1)
xor $t3, $t3, $t2
and $t3, $t0, $t3
sh $t3, %lo(LATBINV)($t1)
swl $t3, %lo(LATGINV)+1($t1)

#20
Page: 1234 > Showing page 1 of 4
Jump to:
© 2020 APG vNext Commercial Version 4.5