Hardware Multiplier

General discussion on mikroC PRO for PIC.
Post Reply
Author
Message
Bill Legge
Posts: 235
Joined: 28 Oct 2007 03:16
Location: West Australia

Hardware Multiplier

#1 Post by Bill Legge » 04 Apr 2022 11:34

I'm using a PIC18F46K22 with a hardware multiplier. The manual gives the asm code as:
MOVF ARG1, W ;
MULWF ARG2 ; ARG1 * ARG2 ->

And the result is found in two 8 bit registers PRODH and PRODL

I'm trying to incorporate it into a C function, something like:

/////////////////////////////////////////////////
// Function - Hardware multiplier for 8bit unsigned bytes
unsigned int Multiply_U8(char ARG1,char ARG2){
asm{
MOVF ARG1, W ;
MULWF ARG1 ; ARG1 * ARG2 ->
}
return((PRODH<<8) + PRODL);
}
/////////////////////////////////////////////////


This does not work I am ignorant of the asm/c code interface - any help please?
Regards Bill Legge in australia

hexreader
Posts: 1786
Joined: 27 Jun 2010 12:07
Location: England

Re: Hardware Multiplier

#2 Post by hexreader » 04 Apr 2022 12:26

It seems to me that asm only works on global variables - could be wrong

Here is my attempt at a solution - not fully tested - only in software debug:
No doubt the code could be improved, but it seems to work

Code: Select all

// PIC18F46K22 on EasyPIC v7 internal oscillator
// not fully tested, but seems to work in simulator debugger

// global variables
unsigned char G_ARG1;
unsigned char G_ARG2;
unsigned int Mul_Result;

/////////////////////////////////////////////////
// Function - Hardware multiplier for 8bit unsigned bytes
unsigned int Multiply_U8(unsigned char ARG1, unsigned char ARG2){
    
    G_ARG1 = ARG1;                                                              // asm seems to work only on global variables
    G_ARG2 = ARG2;

    asm{
        MOVF   _G_ARG1, W, 0                   ; first parameter to W register
        MULWF  _G_ARG2, 0                      ; ARG1 * ARG2
    }
    return((PRODH << 8) + PRODL);
}
/////////////////////////////////////////////////

void main(){

    OSCCON = 0b01000000;                                                        //  HFINTOSC

    C1ON_bit = 0;                                                               // Disable comparators
    C2ON_bit = 0;

    TRISB = 0;                                                                  // all output
    TRISC = 0;
    ANSELB = 0;                                                                 // all digital
    ANSELC = 0;

    Mul_Result = Multiply_U8(7, 9);                                             // expect 7 * 9 = 63
    
    LATB = Mul_Result;                                                          // expect 0x3f
    LATC = Mul_Result >> 8;                                                     // expect 0

    while(1);
}
Start every day with a smile...... (get it over with) :)

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Hardware Multiplier

#3 Post by janni » 04 Apr 2022 18:18

Hi guys,

Simplest way is to let the compiler show you how to do it :) . This

Code: Select all

unsigned int Multiply_U8(char ARG1,char ARG2){
 return ARG1*ARG2;
}
corresponds to assembly in list file

Code: Select all

;test54.c,36 ::                 unsigned int Multiply_U8(char ARG1,char ARG2){
;test54.c,37 ::                 return ARG1*ARG2;
0x005A        0x5015              MOVF        FARG_Multiply_U8_ARG1, 0 
0x005C        0x0216              MULWF       FARG_Multiply_U8_ARG2 
0x005E        0xF000CFF3          MOVFF       PRODL, R0
0x0062        0xF001CFF4          MOVFF       PRODH, R1
;test54.c,43 ::                 }
L_end_Multiply_U8:
0x0066        0x0012              RETURN      0
Naturally, compiler knows where the function parameters are in memory but the user cannot make assumptions, so direct use of this assembly may not always work (when parameters are in another RAM Bank then the current one - same problem with global variables).

Apart from the fact that the idea of using assembly in simple multiplication routine doesn't seem to be sensible :wink: (compiler will always produce as short code and anyway hardware multiplication is so simple that it's best to do it inline, without calling a routine), here's how it can be done

Code: Select all

unsigned int Multiply_U8(char ARG1,char ARG2){
 R0=Arg2;
 WREG=Arg1;
asm{
    MULWF R0
    MOVFF PRODL,R0
    MOVFF PRODH,R1
 }
}
The internal Rx registers are always placed in Access Bank (no bank switching necessary) and compiler uses them for such tasks. The result is passed in R0 and R1 registers, as the compiler would have done (only using few more instructions being confused by our use of R0).

hexreader
Posts: 1786
Joined: 27 Jun 2010 12:07
Location: England

Re: Hardware Multiplier

#4 Post by hexreader » 04 Apr 2022 20:15

Nice one janni :)

Good to see that you are expert in C now.

Your contributions to BASIC and PASCAL are legendary here. Now I can benefit from your new talents with C.

Many thanks
Start every day with a smile...... (get it over with) :)

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Hardware Multiplier

#5 Post by janni » 04 Apr 2022 22:30

You're welcome :D .

BTW, almost 1/3 of my posts are on mC forums, but it always feels nice to be noticed :D .

Bill Legge
Posts: 235
Joined: 28 Oct 2007 03:16
Location: West Australia

Re: Hardware Multiplier

#6 Post by Bill Legge » 05 Apr 2022 00:52

Wow! Thank you both very much.
I'll work on the concept and try to write some demo code based on your advice.
I did not realize that the multiply command '*' used the hardware module.

Regards Bill Legge in Australia

Bill Legge
Posts: 235
Joined: 28 Oct 2007 03:16
Location: West Australia

Re: Hardware Multiplier

#7 Post by Bill Legge » 05 Apr 2022 08:58

Janni - your code works just fine.
And the compiler uses the hardware multiplier as you said.
But I am confused about the asm code.
According to the PIC18F46K22 manual - Instruction Set summary, the operation MULWF:

MULWF Multiply W with f
Syntax: MULWF f {,a}
Operands: 0  f  255
a  [0,1]
Operation: (W) x (f)  PRODH:PRODL


Automatically stores the result in PRODH and PRODL - so why are the last two lines of your code necessary?
MOVFF PRODL,R0
MOVFF PRODH,R1

(They are! - without them the routine does not work!)
But Why?

Regards Bill Legge

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Hardware Multiplier

#8 Post by janni » 05 Apr 2022 12:10

Hi Bill,

I mentioned it in my post - these registers are used by compiler to pass function result. Calling code has no information what the function does inside, but reads result from Rx registers, in this case two of them as the result is of type unsigned int.

Here's how calling a function works:
- compiler forms new variables and stores there function parameters
- function is called, executes its code and places result in Rx registers
- calling code uses the result directly or copies it to assigned variable

Obviously, with inline multiplication the whole hassle is avoided.

Post Reply

Return to “mikroC PRO for PIC General”