abstract: a maxq-based microcontroller can be equipped with a 16x16 hardware multiplier peripheral module. this application note describes how the multiplier operates and how to write code for this module in applications to maximize math performance.
introductionthe hardware multiplier (hereafter a multiply-accumulate, or mac) module is a very powerful tool, especially for applications that require heavy calculations, e.g., firmware for an electricity-metering microcontroller. this multiplier is capable of executing the multiply or multiply-negate or multiply-accumulate or multiply-subtract operation for signed or unsigned operands in a single machine cycle, and even faster for special cases. this article examines the hardware organization and functionality, explains how to write code for mac and provides simple examples of typical calculations using maxq's hardware multiplier.
getting startedto begin, we need basic knowledge about the maxq architecture, register map and instruction set, which information can be obtained from the maxq family user's guide or from any maxq-based microcontroller data sheet, e.g., maxq2000. we also need reference to the mac hardware description, which is in the maxq family user's guide document. certain familiarity with assembly language in general, and with maxq assembler in particular, is assumed.
multiplier overviewfrom the programming point of view, the mac module appears as 8 special function registers, mapped in modules' space from m0 through m5. one register (mcnt) contains control bits, two registers (ma, mb) designated for input operands, three registers (mc0, mc1, mc2 considered as one long mc register) keep the output result, i.e. either product or accumulation, and two read-only registers (mc0r,mc1r considered as one mcr register, r stands for read-only) are used in special cases as output registers. the accumulator mc is configurable, typically 40- or 48-bits wide. in any configuration, there are two 16-bit registers mc0 and mc1, whereas the mc2 register is implemented as 8-bit, 16-bit or other. for the sake of simplicity let us assume that the accumulator mc is 48-bits wide and mac registers are mapped in module m3: #define mcnt m3[0]
#define ma m3[1]
#define mb m3[2]
#define mc2 m3[3]
#define mc1 m3[4]
#define mc0 m3[5]
#define mc1r m3[6]
#define mc0r m3[7]
the multiplier is always ready to do math, usage is very simple and consists of four basic steps: 1) set configuration, 2) load operands, 3) wait, and 4) unload result. figure 1 represents mac registers and typical operation flow. actual calculations start immediately after step 2. when an operation is started, the multiplier calculates the product of operands ma and mb whose product is then either placed or added to the accumulator mc by the end of step 3. the mcr register participates as internal working memory and we normally do not care for it. the way that the register works is rather tricky and confusing at first sight. it is advisable for beginners to ignore the mcr register, with exception for special case mcw=1. advanced user can enjoy the benefits of shorter and faster code by sneaking intermediate data from the mcr register, but such user must fully understand the way it works.
figure 1. hardware multiplier registers structure and typical operation flow.
the set configuration step (step 1) implies a simple write to the control register mcnt. besides updating the data it also implicitly initializes the hardware, i.e., prepares mac to accept new operands and perform new operation. this step can be skipped if desired configuration is already set in the mcnt register. when an operation is started, the multiplier always uses the current content of mcnt.
actual calculations start as soon as the necessary operands are loaded into the mac (step 2). there are no limitations on how quickly data is entered into operand registers or on the order of data entry. the invisible operand count register keeps track of all writes to ma and mb and triggers a calculation accordingly. for example, in two-operand modes, the first loaded operand can then be re-loaded many times without starting a calculation. the calculations are started only when the second operand is loaded. in one-operand modes, the calculations are triggered when the first operand is loaded to the ma or mb register. the operand count gets initialized by hardware when a calculation is started or when the mcnt register is written.
the wait state (step 3) is needed when the result of operation is expected from the mc register. think of it as a two-phase process: 1) execute an operation, e.g. calculate the product ma*mb, and 2) update accumulator mc. the first phase is done right away, but second phase is equivalent to move instruction. it takes one machine cycle of time for mac hardware to execute this move. however, the wait state can be skipped in special case mcw=1 when the result is expected from mcr register, because the output of the first phase is stored there. one can put any meaningful instruction instead of no operation during the wait state, except a write into mcnt, which will disrupt current multiplier operation.
multiplier configuration optionsthe meaning of the control bits in the mcnt register is straightforward.
bit 0: sus treat operands ma, mb as unsigned 16-bit numbers when sus=1; treat operands ma, mb as signed 16-bit numbers when sus=0;
bit 1: mmac add product to accumulator mc when mmac=1; place product into accumulator mc when mmac=0;
bit 2: msub use (-ma) value instead of ma when msub=1; use ma operand as is when msub=0;
bit 3: opsc loading one operand will trigger calculations when opsc=1; loading two operands will trigger calculations when opsc=0;
bit 4: squ loading one operand will copy the loaded value into the other operand register and trigger calculations when squ=1 (square mode); square mode is disabled when squ=0;
bit 5: cld all data registers (ma,mb,mc, operand count) are cleared to 0 when cld=1. this bit is automatically cleared by hardware.
bit 6: mcw prevent multiplier from writing result to accumulator mc when mcw=1. multiplier will write the result into accumulator mc when mcw=0;
bit 7: of read-only bit, signifies that errors (like overflow) occurred when of=1; no errors if of=0; the operand select bit opsc turns on the one-operand mode. the multiplier will start calculation after loading the first operand when opsc=1. the squ bit switches the square mode, in which the first loaded operand is automatically copied to the other operand register and the operation is triggered. the opsc bit has no meaning when squ=1.
the mcw bit is a write-protect flag for the accumulator mc. setting mcw=1 allows performing a multiplication without disturbing the mc register. in this special case the result of operation (to be precise, the least 32 bits of the result) can be obtained without wait state delay from the mcr register. note that the write-protection feature only affects the multiplier operations. user still can write directly to the mc register when mcw=1, but mac cannot update it. that provides a possibility of unconventional use of the mac as an additional storage. five 16-bit registers ma, mb, mc0, mc1, mc2 can temporarily store data, pointers, counters, etc, which is especially useful for the 8-bit maxq10 core. by setting mcw=1 we ensure that the accumulator mc will not be accidentally updated when we write to ma or mb.
the multiplier sets read-only status bit of when critical error happens during operation:
overflow or underflow of mc register for unsigned operation;
overflow or underflow of mc register for signed operation;
attempt to execute unsigned multiply-negate operation. in all other cases the of bit is cleared by hardware after operation. there is no of bit set for multiply-only and signed multiply-negate operation, the bit is cleared. note that in case of overflow/underflow event the mc register contains the correct low bits of the result.
as mentioned above, any write to mcnt also implicitly initializes the operand count register and prepares mac to start new operation. more details about the control bits can be found below in the code examples section.
multiplier data flowthe function of accumulator mc and the read-only register mcr can be presented by the following equations:
where n is the execution cycle, mmac is accumulation flag (0 or 1), msub is negation flag (0 or 1) in the mcnt register. the mcr register keeps only 32 low bits of the right-hand side of the equation (2), but it gets updated immediately at execution cycle n. the mc register keeps a full length of the result, extended with of bit if necessary, but both mc and of get updated 1 cycle later. the data flow in the multiplier during operation is shown on the figure 2.
figure 2. multiplier operation data flow.
code examplesit is convenient to denote the control bits with corresponding names, #define sus 0x01
#define mmac 0x02
#define msub 0x04
#define opsc 0x08
#define squ 0x10
#define cld 0x20
#define mcw 0x40
#define of 0x80
and use those names instead of numbers. for example, the instruction 'move mcnt, #(sus+mmac+msub+squ)' is a mnemonic for 'move m3[0], #23'. programming the multiplier requires the data movement to and from the mac, so the most used instruction is 'move dest,src'. every example in this section is accompanied with a table showing resulting changes in the multiplier registers after execution of each instruction. a bold entry denotes that a register was updated, even if the value is not changed. all numbers are hexadecimal in tables, the 'xxxx' entry denotes the value of no importance. note the behaviour of the mcr register: it gets updated every time the content of ma, or mb, or mc is changed. examples 1-7 illustrate simple use of the mac for a single operation, examples 8-12 show more complex configurations, e.g. how to avoid the wait state for continuous calculations. example 13 demonstrates how the multiplier can speed up a square root calculation.
unsigned multiplication.
calculate the product 3*5 and place it to the register a[0]. move mcnt, #(sus+cld) ; unsigned, multiply-only, clear data
move ma, #3 ; load first operand into ma
move mb, #5 ; load second operand into mb
; the product is in mcr register
nop ; wait for mac to update mc
move a[0],mc0 ; unload the product
the a[0] register contains the product 3*5 = 15 (0x000f).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(sus+cld)
01
0000
0000
0000
0000
0000
0000
0000
move ma, #3
01
0003
0000
0000
0000
0000
0000
0000
move mb, #5
01
0003
0005
0000
0000
0000
0000
000f
nop
01
0003
0005
0000
0000
000f
0000
000f
move a[0],mc0
01
0003
0005
0000
0000
000f
0000
000f
advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop').
signed multiplication.
calculate the product 3*(-5) and place it to the register a[0]. move mcnt, #(cld); signed, multiply-only, clear data registers
move ma, #3 ; load first operand into ma
move mb, #(-5) ; load second operand into mb
; the product is in mcr register
nop ; wait for mac to update mc
move a[0],mc0 ; unload the product
the a[0] register contains the product 3*(-5) = -15 (0xfff1).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(cld)
00
0000
0000
0000
0000
0000
0000
0000
move ma, #3
00
0003
0000
0000
0000
0000
0000
0000
move mb, #(-5)
00
0003
fffb
0000
0000
0000
ffff
fff1
nop
00
0003
fffb
ffff
ffff
fff1
ffff
fff1
move a[0],mc0
00
0003
fffb
ffff
ffff
fff1
ffff
fff1
advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop').
signed multiply-negate operation.
calculate the negative product of 3 and (-5), place it to the register a[0]. move mcnt, #(msub+cld) ; signed, multiply-negate, clear data registers
move ma, #3 ; load first operand into ma
move mb, #(-5) ; load second operand into mb
; the result is in mcr register
nop ; wait for mac to update mc
move a[0],mc0 ; unload the result
the a[0] register contains the result -(3*(-5)) = 15 (0x000f).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(msub+cld)
04
0000
0000
0000
0000
0000
0000
0000
move ma, #3
04
0003
0000
0000
0000
0000
0000
0000
move mb, #(-5)
04
0003
fffb
0000
0000
0000
0000
000f
nop
04
0003
fffb
0000
0000
000f
0000
000f
move a[0],mc0
04
0003
fffb
0000
0000
000f
0000
000f
advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop').
unsigned multiply-accumulate operation.
calculate the expression 2+3*5 and place it to the register a[0]. move mcnt, #(sus+mmac) ; unsigned, multiply-accumulate
; no cld set, data registers hold their content
move mc0, #2 ; pre-load accumulator
move mc1, #0 ; pre-load accumulator
move mc2, #0 ; pre-load accumulator
move ma, #3 ; load first operand into ma
move mb, #5 ; load second operand into mb
; the result is in mcr register!
nop ; wait for mac to update mc
; the mcr was changed to mc+ma*mb!
move a[0],mc0 ; unload the result
the a[0] register contains the result 2+3*5 = 17 (0x0011).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(sus+mmac)
03
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mc0, #2
03
xxxx
xxxx
xxxx
xxxx
0002
xxxx
xxxx
move mc1, #0
03
xxxx
xxxx
xxxx
0000
0002
xxxx
xxxx
move mc2, #0
03
xxxx
xxxx
0000
0000
0002
xxxx
xxxx
move ma, #3
03
0003
xxxx
0000
0000
0002
xxxx
xxxx
move mb, #5
03
0003
0005
0000
0000
0002
0000
0011
nop
03
0003
0005
0000
0000
0011
0000
0020
move a[0],mc0
03
0003
0005
0000
0000
0011
0000
0020
note the behavior of the mcr register. it follows the equation (2) above, i.e. always reflects the current state of ma, mb, and mc. in this example, the mcr register holds 32 bits of the result only 1 machine cycle during wait state, then changes its value because the mc register gets updated after the wait state, the new value is mcr = 0x0020 = 32 = 17+3*5 = mc+ma*mb.
advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop'). but two things must be remembered: i) there are only 32 low bits of the result available from the mcr, and ii) even those 32 bits are available limited time, only one machine cycle in this example configuration.
signed multiply-accumulate operation.
calculate the expression (-2)+3*(-5) and place it to the register a[0]. move mcnt, #(mmac) ; signed, multiply-accumulate
move mc0, #0fffeh ; pre-load accumulator
move mc1, #0ffffh ; pre-load accumulator
move mc2, mc1 ; pre-load accumulator
move ma, #3 ; load first operand into ma
move mb, #(-5) ; load second operand into mb
; the result is in mcr register!
nop ; wait for mac to update mc
; the mcr was changed to mc+ma*mb!
move a[0],mc0 ; unload the result
the a[0] register contains the result (-2)+3*(-5) = -17 (0xffef).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(mmac)
02
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mc0, #0fffeh
02
xxxx
xxxx
xxxx
xxxx
fffe
xxxx
xxxx
move mc1, #0ffffh
02
xxxx
xxxx
xxxx
ffff
fffe
xxxx
xxxx
move mc2, mc1
02
xxxx
xxxx
ffff
ffff
fffe
xxxx
xxxx
move ma, #3
02
0003
xxxx
ffff
ffff
fffe
xxxx
xxxx
move mb, #(-5)
02
0003
fffb
ffff
ffff
fffe
ffff
ffef
nop
02
0003
fffb
ffff
ffff
ffef
ffff
ffe0
move a[0],mc0 02 0003 fffb ffff ffff ffef ffff ffe0
as in example 4 above, advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop') before this register gets updated with new value mcr = 0xffff ffe0 = -32 = -17+3*(-5) = mc+ma*mb.
unsigned multiply-subtract operation.
calculate the expression 17-3*5 and place it to the register a[0]. let us assume that the operands are stored in the registers a[1]=3 and a[2]=5. move mcnt, #(sus+mmac+msub) ; unsigned, multiply-subtract
move mc0, #17 ; pre-load accumulator
move mc1, #0 ; pre-load accumulator
move mc2, #0 ; pre-load accumulator
move ma, a[1] ; load first operand into ma
move mb, a[2] ; load second operand into mb
; the result is in mcr register!
nop ; wait for mac to update mc
; the mcr was changed to mc-ma*mb!
move a[0],mc0 ; unload the result
the a[0] register contains the result 17-3*5 = 2 (0x0002).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(sus+mmac+msub)
07
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mc0, #17
07
xxxx
xxxx
xxxx
xxxx
0011
xxxx
xxxx
move mc1, #0
07
xxxx
xxxx
xxxx
0000
0011
xxxx
xxxx
move mc2, #0
07
xxxx
xxxx
0000
0000
0011
xxxx
xxxx
move ma, a[1]
07
0003
xxxx
0000
0000
0011
xxxx
xxxx
move mb, a[2]
07
0003
0005
0000
0000
0011
0000
0002
nop
07
0003
0005
0000
0000
0002
ffff
fff3
move a[0],mc0
07
0003
0005
0000
0000
0002
ffff
fff3
as in example 4 above, advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop') before this register gets updated with new value mcr = 0xffff fff3 = -13 = 2-3*5 = mc-ma*mb.
signed multiply-subtract operation.
calculate the expression (_2)-3*(-5) and place it to the register a[0]. let us assume that the operands are stored in the registers a[1]=3 and a[2]= -5. move mcnt, #(mmac+msub); signed, multiply-subtract
move mc0, #0fffeh ; pre-load accumulator
move mc1, #0ffffh ; pre-load accumulator
move mc2, #0ffffh ; pre-load accumulator
move ma, a[1] ; load first operand into ma
move mb, a[2] ; load second operand into mb
; the result is in mcr register!
nop ; wait for mac to update mc
; the mcr was changed to mc-ma*mb!
move a[0],mc0 ; unload the result
the a[0] register contains the result (-2)-3*(-5) = 13 (0x000d).
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt,#(sus+mmac+msub)
06
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mc0, #0fffeh
06
xxxx
xxxx
xxxx
xxxx
fffe
xxxx
xxxx
move mc1, #0ffffh
06
xxxx
xxxx
xxxx
ffff
fffe
xxxx
xxxx
move mc2, #0ffffh
06
xxxx
xxxx
ffff
ffff
fffe
xxxx
xxxx
move ma, a[1]
06
0003
xxxx
ffff
ffff
fffe
xxxx
xxxx
move mb, a[2]
06
0003
fffb
ffff
ffff
fffe
0000
000d
nop
06
0003
fffb
0000
0000
000d
0000
001c
move a[0],mc0
06
0003
fffb
0000
0000
000d
0000
001c
as in example 4 above, advanced user may save a cycle by reading from the mcr ('move a[0],mc0r' instead of 'nop') before this register gets updated with new value mcr = 0x001c = 28 = 13-3*(-5) = mc-ma*mb.
one-operand mode.
this mode is useful when an application needs to perform an operation with the same constant many times. since one of the operands is not changing, no need to load it every time. just load the constant operand once and switch the multiplier to the one-operand mode. then every write to another operand register will trigger the calculations.
for example, assume that analog-to-digital conversion hardware repeatedly returns voltage as 16-bit values in a register called adc with the least significant bit scaled as 1/8 v, and a microcontroller application must re-calculate every voltage value to mv units and store it in data ram. that re-calculation can be done by multiplying the row data by coefficient 1000/8=125 (0x7d). following code will do the job.
; configure multiplier initially (this code is executed once)
move ma, #125 ; load the constant factor into ma.
move mcnt, #(opsc) ; signed, multiply-only, one-operand mode
......
; this code is executed repeatedly with every new adc value
move mb, adc ; load operand into mb
; the result is in mcr register!
move dp[0],#8 ; set address=0x0008 while waiting for mac to update mc
move @dp[0], mc0 ; store the result in data memory at address 0x0008
......
the example shows, by the way, how the wait state can be used for setting an address instead of wasting time with nop. let assume that three consecutive raw voltages were 5.0v, 1.5v, -2.5v (0x0028, 0x000c, 0xffec) then software flow can be represented by following table.
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move ma, #125
xxxx
007d
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mcnt,#(opsc)
08
007d
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
.....
move mb, adc
08
007d
0028
xxxx
xxxx
xxxx
0000
1388
move dp[0], #8
08
007d
0028
0000
0000
1388
0000
1388
move @dp[0], mc0
08
007d
0028
0000
0000
1388
0000
1388
.....
data memory[8] = 0x1388 (= 5000 mv)
move mb, adc
08
007d
000c
xxxx
xxxx
xxxx
0000
05dc
move dp[0], #8
08
007d
000a
0000
0000
05dc
0000
05dc
move @dp[0], mc0
08
007d
000a
0000
0000
05dc
0000
05dc
.....
data memory[8] = 0x05dc (= 1500 mv)
move mb, adc
08
007d
ffec
xxxx
xxxx
xxxx
ffff
f63c
move dp[0], #8
08
007d
ffec
ffff
ffff
f63c
ffff
f63c
move @dp[0], mc0
08
007d
ffec
ffff
ffff
f63c
ffff
f63c
.....
data memory[8] = 0xf63c (= -2500 mv)
again, advanced user may save a cycle of the repeated code by reading from the mcr: ; configure multiplier initially (this code is executed once)
move dp[0],#8 ; set address=0x0008
move ma, #125 ; load the constant factor into ma.
move mcnt, #(opsc) ; signed, multiply-only, one-operand mode
......
; this code is executed repeatedly with every new adc value
move mb, adc ; load operand into mb
; the result is in mcr register!
move @dp[0], mc0r; store the result in data memory at address 0x0008
......
but remember the limitations: only 32 low bits are available from the mcr, and its content is subject to change without notice!
square mode.
in this mode, a square can be calculated by loading only 1 operand. for example, a microcontroller application must calculate rms value of the voltage. part of that calculation is a sum of squares of the samples, which sum can be easily done by using mac's square mode. assume the voltage samples are 16-bit signed values scaled in mv units and stored (repeatedly) in data ram at address 0x0008. the code might look as follows. ; configure multiplier initially (this code is executed once)
move dp[0],#8 ; set address=0x0008
move mcnt,#(mmac+squ+cld) ; signed, square-accumulate, clear registers
......
; this code is executed repeatedly with every new sample
move ma, @dp[0] ; load operand into ma (assume dp[0] is active data ptr)
; the result is in mcr register!
nop ; wait for mac to update mc
; the mcr was changed to mc+ma*mb!
......
the sum of squares is being accumulated in the mc register. assume that the first three consecutive raw voltages were 5.0v, 1.5v, -2.5v (0x1388, 0x05dc, 0xf63c), then software flow can be represented by following table.
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move dp[0], #8
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mcnt,#(mmac+squ+cld)
12
0000
0000
0000
0000
0000
0000
0000
.....
move ma, @dp[0]
12
1388
1388
0000
0000
0000
017d
7840
nop
12
1388
1388
0000
017d
7840
02fa
f080
.....
mc = 0x017d 7840 (= 25 000 000 mv2)
move ma, @dp[0]
12
05dc
05dc
0000
017d
7840
019f
cd50
nop
12
05dc
05dc
0000
019f
cd50
01c2
2260
.....
mc = 0x019f cd50 (= 25 000 000 + 2 250 000 mv2)
move ma, @dp[0]
12
f63c
f63c
0000
019f
cd50
01ff
2b60
nop
12
f63c
f63c
0000
01ff
2b60
025e
8970
.....
mc = 0x01ff 2b60 (= 25 000 000 + 2 250 000 + 6 250 000 mv2)
accumulator write-protect feature.
this feature (mcw control bit) allows a multiply without accumulate, i.e. the contents of the accumulator are not updated by multiplier's operation. it may be useful if application requires to perform two different tasks in parallel, one involves accumulation and another one is just to multiply numbers. then the multiplier can be switched back and forth between those tasks without saving/restoring its accumulator. say, we can combine the above examples 8 and 9 to accumulate the sum of squares in parallel with conversion to millivolts by using mcw control bit. ; initialize mac (this code is executed once)
move dp[0],#8 ; set address=0x0008
move mcnt,#(cld) ; clear registers
......
; this code is executed repeatedly with every new adc value
; multiply by constant factor but keep the mc intact
move mcnt, #(mcw); signed, multiply-only, mcw-protect
move ma, #125 ; load constant factor into ma
move mb, adc ; load raw sample into mb
; the result is in mcr register!
; special case mcw=1, no wait state needed here!
move @dp[0],mc0r ; store the result in data memory at address 0x0008
; accumulate sum of squares in mc
move mcnt, #(mmac+squ) ; signed, square-accumulate, clear mcw-protect
move ma, mc0r ; load operand into ma
; the result is in mcr register!
nop ; wait for mac to update mc
; the mcr was changed to mc+ma*mb!
......
the sum of squares is being accumulated in mc register, while result of multiplication (conversion to mv) is accessible via mcr register. note that no wait state is necessary for the multiplication. let assume that the first three consecutive raw voltages were 5.0v, 1.5v, -2.5v (0x0028, 0x000c, 0xffec) then software flow can be represented by following table.
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move dp[0], #8
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
xxxx
move mcnt,#(cld)
00
0000
0000
0000
0000
0000
0000
0000
.....
move mcnt, #(mcw)
40
xxxx
xxxx
0000
0000
0000
xxxx
xxxx
move ma, #125
40
007d
xxxx
0000
0000
0000
xxxx
xxxx
move mb, adc
40
007d
0028
0000
0000
0000
0000
1388
move @dp[0], mc0r
40
007d
0028
0000
0000
0000
0000
1388
move mcnt, #(mmac+squ)
12
007d
0028
0000
0000
0000
0000
1388
move ma, mc0r
12
1388
1388
0000
0000
0000
017d
7840
nop
12
1388
1388
0000
017d
7840
02fa
f080
.....
data memory[8] = 0x1388 (= 5000 mv)mc = 0x017d 7840 (= 25 000 000 mv2)
move mcnt, #(mcw)
40
xxxx
xxxx
0000
017d
7840
xxxx
xxxx
move ma, #125
40
007d
xxxx
0000
017d
7840
xxxx
xxxx
move mb, adc
40
007d
000c
0000
017d
7840
0000
05dc
move @dp[0], mc0r
40
007d
000c
0000
017d
7840
0000
05dc
move mcnt, #(mmac+squ)
12
007d
000c
0000
017d
7840
0000
05dc
move ma, mc0r
12
05dc
05dc
0000
017d
7840
019f
cd50
nop
12
05dc
05dc
0000
019f
cd50
01c2
2260
.....
data memory[8] = 0x05dc (= 1500 mv)mc = 0x019f cd50 (= 25 000 000 + 2 250 000 mv2)
move mcnt, #(mcw)
40
xxxx
xxxx
0000
019f
cd50
xxxx
xxxx
move ma, #125
40
007d
xxxx
0000
019f
cd50
xxxx
xxxx
move mb, adc
40
007d
ffec
0000
019f
cd50
ffff
f63c
move @dp[0], mc0r
40
007d
ffec
0000
019f
cd50
ffff
f63c
move mcnt, #(mmac+squ)
12
007d
ffec
0000
019f
cd50
ffff
f63c
move ma, mc0r
12
f63c
f63c
0000
019f
cd50
01ff
2b60
nop
12
05dc
05dc
0000
01ff
2b60
025e
8970
.....
data memory[8] = 0xf63c (= -2500 mv)mc = 0x01ff 2b60 (= 25 000 000 + 2 250 000 + 6 250 000 mv2)
continuous (back-to-back) calculations.
the wait state can be used for loading an operand for the next operation without disrupting current operation. for example, if the sum s=1*2+3*4+5*6+7*8+9*10 is to be calculated, that could be done by the following code. move mcnt,#(mmac+cld) ; signed, multiply-accumulate, clear registers
move ma, #1 ; load operand ma
move mb, #2 ; load operand mb, trigger 1*2
move ma, #3 ; load operand ma while waiting for mac to complete 1*2
move mb, #4 ; load operand mb, trigger 3*4
move ma, #5 ; load operand ma while waiting for mac to complete 3*4
move mb, #6 ; load operand mb, trigger 5*6
move ma, #7 ; load operand ma while waiting for mac to complete 5*6
move mb, #8 ; load operand mb, trigger 7*8
move ma, #9 ; load operand ma while waiting for mac to complete 7*8
move mb, #10 ; load operand mb, trigger 9*10
nop ; wait for mac to update mc
the sum s=190 (0xbe) is in mc register. note that all wait states except last one were not wasted but used for loading operands in this example.
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt, #(mmac+cld)
02
0000
0000
0000
0000
0000
0000
0000
move ma, #1
02
0001
0000
0000
0000
0000
0000
0000
move mb, #2
02
0001
0002
0000
0000
0000
0000
0002
move ma, #3
02
0003
0002
0000
0000
0002
0000
0008
move mb, #4
02
0003
0004
0000
0000
0002
0000
000e
move ma, #5
02
0005
0004
0000
0000
000e
0000
0022
move mb, #6
02
0005
0006
0000
0000
000e
0000
002c
move ma, #7
02
0007
0006
0000
0000
002c
0000
0056
move mb, #8
02
0007
0008
0000
0000
002c
0000
0064
move ma, #9
02
0009
0008
0000
0000
0064
0000
00ac
move mb, #10
02
0009
000a
0000
0000
0064
0000
00be
nop
02
0009
000a
0000
0000
00be
0000
0118
similar technique can be utilized in one-operand or square mode. for example, if the sum s=1²+2²+3²+4²+5²+6²+7² is to be calculated, it can be done with following code. move mcnt,#(mmac+squ+cld) ; signed, square-accumulate, clear registers
move ma, #1 ; load operand into ma, trigger 1*1
move ma, #2 ; load operand into ma, trigger 2*2 while waiting
move ma, #3 ; load operand into ma, trigger 3*3 while waiting
move ma, #4 ; load operand into ma, trigger 4*4 while waiting
move ma, #5 ; load operand into ma, trigger 5*5 while waiting
move ma, #6 ; load operand into ma, trigger 6*6 while waiting
move ma, #7 ; load operand into ma, trigger 7*7 while waiting
nop ; wait for mac to update mc
the sum s=140 (0x8c) is in mc register. note that all wait states except last one were not wasted but used for loading operands.
instruction
mcnt
ma
mb
mc2
mc1
mc0
mc1r
mc0r
move mcnt, #(mmac+squ+cld)
12
0000
0000
0000
0000
0000
0000
0000
move ma, #1
12
0001
0001
0000
0000
0000
0000
0001
move ma, #2
12
0002
0002
0000
0000
0001
0000
0005
move ma, #3
12
0003
0003
0000
0000
0005
0000
000e
move ma, #4
12
0004
0004
0000
0000
000e
0000
001e
move ma, #5
12
0005
0005
0000
0000
001e
0000
0037
move ma, #6
12
0006
0006
0000
0000
0037
0000
005b
move ma, #7
12
0007
0007
0000
0000
005b
0000
008c
nop
12
0007
0007
0000
0000
008c
0000
00bd
overflow.
in case of overflow/underflow event the mc register contains the correct low bits of the result, so the of flag can be used as multiplier's carry/borrow bit to implement an accumulator which is longer than mc. for example, a 64-bit accumulator could consist of 16-bit register a[0] and 48-bit mc register. then 64-bit multiply-accumulate operation could be done by following code. move mcnt, #(sus+mmac) ; unsigned, multiply-accumulate
move ma, #32768 ; load operand into ma
move mb, #16384 ; load operand into mb
nop ; wait for mac to update mc
; the of flag is set (if necessary)
move c, mcnt.7 ; copy of bit into carry
addc #0 ; a[0]+=0+carry (assume a[0] is active maxq's accumulator)
the improvised 64-bit accumulator is incremented by 32768*16384. let assume the initial value was 0x1234 ffff f000 4321, then software flow is represented by the following table, new value is 0x1235 0000 1000 4321 = old value + 0x2000 0000.
instruction
mcnt
ma
mb
carry
a[0]
mc2
mc1
mc0
move mcnt,#(sus+mmac)
03
xxxx
xxxx
xxxx
1234
ffff
4321
move ma, #32768
03
8000
xxxx
xxxx
1234
ffff
f000
4321
move mb, #16384
03
8000
4000
xxxx
1234
ffff
f000
4321
nop
83
8000
4000
xxxx
1234
0000
1000
4321
move c, mcnt.7
83
8000
4000
1
1234
0000
1000
4321
addc #0
83
8000
4000
0
1235
0000
1000
4321
square root subroutine.
the mac module can help to speed up with various math calculations, not only multiplication and accumulation. for example, examine a subroutine which calculates the square root of an unsigned 32-bit number a. this subroutine finds the result, an unsigned 16-bit number x such that x²≤a= 1 (shift mask right, lsb -> carry)
sjump nc,sqrt32mac_maxq20_loop ; next iteration if no carry
; all 16 bits done, exit
ret ; return
; end sqrt32mac subroutine for maxq20
;========================================
the table below demonstrates few last iterations for a=0x00aa 63cb. the number comes from the above example 9 and represents the voltage mean square value (=0x01ff 2b60/3). the square root from this number is the rms voltage in mv units. the star symbol (*) denotes the active maxq's accumulator, a[0] or a[1]. the result x=0x0d0d (=3341 mv) is in a[0] register in the end.
instruction
a[0] (x)
a[1] (mask)
mcnt
ma
mb
mc2
mc1
mc0
.......
; iteration 14:
move apc, #080h
*0d08
0004
17
0d08
0d08
0000
0000
938b
or a[1]
*0d0c
0004
17
0d08
0d08
0000
0000
938b
move mc2, #0
*0d0c
0004
17
0d08
0d08
0000
0000
938b
move mc1, a[3]
*0d0c
0004
17
0d08
0d08
0000
00aa
938b
move mc0, a[2]
*0d0c
0004
17
0d08
0d08
0000
0000
63cb
move ma, a[0]
*0d0c
0004
17
0d0c
0d0c
0000
0000
63cb
nop
*0d0c
0004
17
0d0c
0d0c
0000
0000
2b3b
move c,mc2.0
carry = 0
sjump nc,
jump (hit)
move ap,#01
0d0c
*0004
17
0d0c
0d0c
0000
0000
2b3b
sr
0d0c
*0002
carry = 0
jump nc,
jump to next iteration
; iteration 15:
move apc, #080h
*0d0c
0002
17
0d0c
0d0c
0000
0000
2b3b
or a[1]
*0d0e
0002
17
0d0c
0d0c
0000
0000
2b3b
move mc2, #0
*0d0e
0002
17
0d0c
0d0c
0000
0000
2b3b
move mc1, a[3]
*0d0e
0002
17
0d0c
0d0c
0000
00aa
2b3b
move mc0, a[2]
*0d0e
0002
17
0d0c
0d0c
0000
0000
63cb
move ma, a[0]
*0d0e
0002
17
0d0e
0d0e
0000
0000
63cb
nop
*0d0e
0002
17
0d0e
0d0e
ffff
ffff
f707
move c,mc2.0
carry = 1
jump nc,
no jump (miss)
xor a[1]
*0d0c
0002
17
0d0e
0d0e
ffff
ffff
f707
move ap,#01
0d0c
*0002
17
0d0e
0d0e
ffff
ffff
f707
sr
0d0c
*0001
carry = 0
sjump nc,
jump to next iteration
; iteration 16:
move apc, #080h
*0d0c
0001
17
0d0e
0d0e
ffff
ffff
f707
or a[1]
*0d0d
0001
17
0d0e
0d0e
ffff
ffff
f707
move mc2, #0
*0d0d
0001
17
0d0e
0d0e
0000
ffff
f707
move mc1, a[3]
*0d0d
0001
17
0d0e
0d0e
0000
00aa
f707
move mc0, a[2]
*0d0d
0001
17
0d0e
0d0e
0000
0000
63cb
move ma, a[0]
*0d0d
0001
17
0d0d
0d0d
0000
0000
63cb
nop
*0d0d
0001
17
0d0d
0d0d
0000
0000
1122
move c,mc2.0
carry = 0
jump nc,
jump (hit)
move ap,#01
0d0d
*0001
17
0d0d
0d0d
0000
0000
1122
sr
0d0d
*0000
carry = 1
sjump nc,
no jump to next iteration
ret
return with a[0]= sqrt(a[3:2])
关于HDFS的概述及组成与架构详解
NFC用电感器的选择及使用方法要点
人工智能模型服务商“布尔数据”完成数千万元A轮融资
powerpcb:多层板减为两层板的方法
SMT加工焊盘翘起是什么原因导致的?
用MAXQ的倍增器模块-Using MAXQ's M
导致线束连接器端子发生退针问题的三个因素
弹片微针模组可有效解决触摸屏的测试难题
2N3055可变电源电路原理图
近2万亿元减税降费红利正在加速注入实体!科技型企业喜获红利
土壤分析仪器的简单介绍
曙光建设的首座液冷数据中心已运行2500天
北京大学电子设计自动化研究院挂牌成立,行芯科技与北大联手建设联合实验
身份认证带动了怎样的网络安全趋势
锂电池产品性能改良或能加速新能源汽车发展
医疗用途的流体测量及阻抗测量的各个应用以及多功能性
中国移动咪咕公司正式发布了新华网咪咕5G富媒体实验室项目
搭载紫光展锐5G芯T760,中兴远航40火热开售
CTEX 2019盛大展出 500家企业品牌展现PCB产业链最新动态
为什么buck电路中开关器多用mosfet而不用bjt?