Scalar unit¶
Contents
- Scalar unit
- Introduction
- Instruction format
- Instructions
- Load immediate: mov
- Set high bits: sethi
- Move to/from other register file: mov
- Arithmetic operations: mul, min, max, abs, neg, add, sub, shr, sar
- Bit operations: bitop
- Bit operations with immediate: and, or, xor
- Simple bytewise operations: bmin, bmax, babs, bneg, badd, bsub
- Bytewise bit operations: band, bor, bxor
- Bytewise bit shift operations: bshr, bsar
- Bytewise multiplication: bmul
- Send immediate to vector unit: vec
- Send mask to vector unit and shift: vecms
- Send bytes to vector unit: bvec
- Bytewise multiply, add, and send to vector unit: bvecmad, bvecmadsel
Introduction¶
The scalar unit is one of the four execution units of VP1. It is used for general-purpose arithmetic.
Scalar registers¶
The scalar unit has 31 GPRs, $r0-$r30
. They are 32 bits wide, and are
usually used as 32-bit integers, but there are also SIMD instructions treating
them as arrays of 4 bytes. In such cases, array notation is used to denote
the individual bytes. Bits 0-7 are considered to be $rX[0]
, bits 8-15 are
$rX[1]
and so on. $r31
is a special register hardwired to 0.
There are also 8 bits in each $c
register belonging to the scalar unit.
Most scalar instructions can (if requested) set these bits according to the
computation result. The bits are:
- bit 0: sign flag - set equal to bit 31 of the result
- bit 1: zero flag - set if the result is 0
- bit 2: b19 flag - set equal to bit 19 of the result
- bit 3: b20 difference flag - set if bit 20 of the result is different from bit 20 of the first source
- bit 4: b20 flag - set equal to bit 20 of the result
- bit 5: b21 flag - set equal to bit 21 of the result
- bit 6: alt b19 flag (G80 only) - set equal to bit 19 of the result
- bit 7: b18 flag (G80 only) - set equal to bit 18 of the result
The purpose of the last 6 bits is so far unknown.
Scalar to vector data bus¶
In addition to performing computations of its own, the scalar unit is also used in tandem with the vector unit to perform complex instructions. Certain scalar opcodes expose data on so-called s2v path (scalar to vector data bus), and certain vector opcodes consume this data.
The data is ephemeral and only exists during the execution of a single bundle - the producing and consuming instructions must be located in the same bundle. If a consuming instruction is used without a producing instruction, it’ll read junk. If a producing instruction is used without a consuming instruction, the data is discarded.
The s2v data consists of:
- 4 signed 10-bits factors, used for multiplication
$vc
selection and transformation, for use as mask input in vector unit, made of:- valid flag: 1 if s2v data was emitted by proper s2v-emitting instruction (if false, vector unit will use an alternate source not involving s2v)
- 2-bit
$vc
register index - 1-bit zero flag or sign flag selection (selects which half of
$vc
will be used) - 3-bit transform mode: used to mangle the
$vc
value before use as mask
The factors can alternatively be treated as two 16-bit masks by some instructions. In that case, mask 0 consists of bits 1-8 of factor 0, then bits 1-8 of factor 1 and mask 1 likewise consists of bits 1-8 of factors 2 and 3:
s2v.mask[0] = (s2v.factor[0] >> 1 & 0xff) | (s2v.factor[1] >> 1 & 0xff) << 8
s2v.mask[1] = (s2v.factor[2] >> 1 & 0xff) | (s2v.factor[3] >> 1 & 0xff) << 8
The $vc
based mask is derived as follows:
def xfrm(val, tab):
res = 0
for idx in range(16):
# bit x of result is set if bit tab[x] of input is set
if val & 1 << tab[idx]:
res |= 1 << idx
return res
val = $vc[s2v.vcsel.idx]
# val2 is only used for transform mode 7
val2 = $vc[s2v.vcsel.idx | 1]
if s2v.vcsel.flag == 'sf':
val = val & 0xffff
val2 = val2 & 0xffff
else: # 'zf'
val = val >> 16 & 0xffff
val2 = val2 >> 16 & 0xffff
if s2v.vcsel.xfrm == 0:
# passthrough
s2v.vcmask = val
elif s2v.vcsel.xfrm == 1:
s2v.vcmask = xfrm(val, [2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, 14, 14])
elif s2v.vcsel.xfrm == 2:
s2v.vcmask = xfrm(val, [4, 5, 4, 5, 4, 5, 4, 5, 12, 13, 12, 13, 12, 13, 12, 13])
elif s2v.vcsel.xfrm == 3:
s2v.vcmask = xfrm(val, [0, 0, 2, 0, 4, 4, 6, 4, 8, 8, 10, 8, 12, 12, 14, 12])
elif s2v.vcsel.xfrm == 4:
s2v.vcmask = xfrm(val, [1, 1, 1, 3, 5, 5, 5, 7, 9, 9, 9, 11, 13, 13, 13, 15])
elif s2v.vcsel.xfrm == 5:
s2v.vcmask = xfrm(val, [0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14])
elif s2v.vcsel.xfrm == 6:
s2v.vcmask = xfrm(val, [1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13])
elif s2v.vcsel.xfrm == 7:
# mode 7 is special: it uses two $vc inputs and takes every second bit
s2v.vcmask = xfrm(val | val2 << 16, [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30])
Instruction format¶
The instruction word fields used in scalar instructions are:
- bits 0-2:
CDST
- if < 4, index of the$c
register to set according to the instruction’s result. Otherwise, an indication that$c
is not to be written (nVidia appears to use 7 in such case). - bits 0-7:
BIMMBAD
- an immediate field used only in bad opcodes - bits 0-18:
IMM19
- a signed 19-bit immediate field used only by the mov instruction - bits 0-15:
IMM16
- a 16-bit immediate field used only by the sethi instruction - bits 1-9:
FACTOR1
- a 9-bit signed immediate used as vector factor - bits 10-18:
FACTOR2
- a 9-bit signed immediate used as vector factor - bit 1:
SIGN2
- determines if byte multiplication source 2 is signed- 0:
u
- unsigned - 1:
s
- signed
- 0:
- bit 2:
SIGN1
- likewise for source 1 - bits 3-10:
BIMM
: an 8-bit immediate for bytewise operations, signed or unsigned depending on instruction. - bits 3-13:
IMM
: signed 13-bit immediate. - bits 3-6:
BITOP
: selects the bit operation to perform - bits 3-7:
RFILE
: selects the other register file for mov to/from other register file - bits 3-4:
COND
- if source mangling is used, the$c
register index to use for source mangling. - bits 5-8:
SLCT
- if source mangling is used, the condition to use for source mangling. - bit 8:
RND
- determines byte multiplication rounding behaviour- 0:
rd
- round down - 1:
rn
- round to nearest, ties rounding up
- 0:
- btis 9-13:
SRC2
- the second source$r
register, often mangled via source mangling. - bits 9-13 (low 5 bits) and bit 0 (high bit):
BIMMMUL
- a 6-bit immediate for bytewise multiplication, signed or unsigned depending on instruction. - bits 14-18:
SRC1
- the first source$r
register. - bits 19-23:
DST
- the destination$r
register. - bits 19-20:
VCIDX
- the$vc
register index for s2v - bit 21:
VCFLAG
- the$vc
flag selection for s2v:- 0:
sf
- 1:
zf
- 0:
- bits 22-23 (low part) and 0 (high part):
VCXFRM
- the$vc
transformation for s2v - bits 24-31:
OP
- the opcode.
Opcodes¶
The opcode range assigned to the scalar unit is 0x00-0x7f
. The opcodes are:
0x01
,0x11
,0x21
,0x31
: bytewise multiplication: bmul0x02
,0x12
,0x22
,0x32
: bytewise multiplication: bmul (bad opcode)0x04
: s2v multiply/add/send: bvecmad0x24
: s2v immediate send: vec0x05
: s2v multiply/add/select/send: bvecmadsel0x25
: bytewise immediate and: band0x26
: bytewise immediate or: bor0x27
: bytewise immediate xor: bxor0x08
,0x18
,0x28
,0x38
: bytewise minimum: bmin0x09
,0x19
,0x29
,0x39
: bytewise maximum: bmax0x0a
,0x1a
,0x2a
,0x3a
: bytewise absolute value: babs0x0b
,0x1b
,0x2b
,0x3b
: bytewise negate: bneg0x0c
,0x1c
,0x2c
,0x3c
: bytewise addition: badd0x0d
,0x1d
,0x2d
,0x3d
: bytewise substract: bsub0x0e
,0x1e
,0x2e
,0x3e
: bytewise shift: bshr, bsar0x0f
: s2v send: bvec0x41
,0x51
,0x61
,0x71
: 16-bit multiplication: mul0x42
: bitwise operation: bitop0x62
: immediate and: and0x63
: immediate xor: xor0x64
: immediate or: or0x45
: s2v 4-bit mask send and shift: vecms0x65
: load immediate: mov0x75
: set high bits immediate: sethi0x6a
: mov to other register file: mov0x6b
: mov from other register file: mov0x48
,0x58
,0x68
,0x78
: minimum: min0x49
,0x59
,0x69
,0x79
: maximum: max0x4a
,0x5a
,0x7a
: absolute value: abs0x4b
,0x5b
,0x7b
: negation: neg0x4c
,0x5c
,0x6c
,0x7c
: addition: add0x4d
,0x5d
,0x6d
,0x7d
: substraction: sub0x4e
,0x5e
,0x6e
,0x7e
: shift: shr, sar0x4f
: the canonical scalar nop opcode
Todo
some unused opcodes clear $c, some don’t
Bad opcodes¶
Some of the VP1 instructions look like they’re either buggy or just unintended artifacts of incomplete decoding hardware. These are known as bad opcodes and are characterised by using colliding bitfields. It’s probably a bad idea to use them, but they do seem to reliably perform as documented here.
Source mangling¶
Some instructions perform source mangling: the source register(s) they use are not taken directly from a register index bitfield in the instruction. Instead, the register index from the instruction is… “adjusted” before use. There are several algorithms used for source mangling, most of them used only in a single instruction.
The most common one, known as SRC2S
, takes the register index from
SRC2
field, a $c
register index from COND
, and $c
bit index
from SLCT
. If SLCT
is anything other than 4, the selected bit is
extracted from $c
and XORed into the lowest bit of the register index
to use. Otherwise (SLCT
is 4), bits 4-5 of $c
are extracted, and
added to bits 0-1 of the register index, discarding overflow out of bit 1:
if SLCT == 4:
adjust = $c[COND] >> 4 & 3
SRC2S = (SRC2 & ~3) | ((SRC2 + adjust) & 3)
else:
adjust = $c[COND] >> SLCT & 1
SRC2S = SRC2 ^ adjust
Instructions¶
Load immediate: mov¶
Loads a 19-bit signed immediate to the selected register. If you need to load a const that doesn’t fit into 19 signed bits, use this instruction along with sethi.
- Instructions:
Instruction Operands Opcode mov
$r[DST] IMM19
0x65
- Operation:
$r[DST] = IMM19
Set high bits: sethi¶
Loads a 16-bit immediate to high bits of the selected register. Low 16 bits are unaffected.
- Instructions:
Instruction Operands Opcode sethi
$r[DST] IMM16
0x75
- Operation:
$r[DST] = ($r[DST] & 0xffff) | IMM16 << 16
Move to/from other register file: mov¶
Does what it says on the tin. There is $c
output capability, but it
always outputs 0. The other register file is selected by RFILE
field,
and the possibilities are:
- 0:
$v
word 0 (ie. bytes 0-3) - 1:
$v
word 1 (bytes 4-7) - 2:
$v
word 2 (bytes 8-11) - 3:
$v
word 3 (bytes 12-15) - 4: ??? (NV41:G80 only)
- 5: ??? (NV41:G80 only)
- 6: ??? (NV41:G80 only)
- 7: ??? (NV41:G80 only)
- 8:
$sr
- 9:
$mi
- 10:
$uc
- 11:
$l
(indices over 3 are ignored on writes, wrapped modulo 4 on reads) - 12:
$a
- 13:
$c
- read only (indices over 3 read as 0) - 18: curiously enough, aliases 2, for writes only
- 20:
$m[0-31]
- 21:
$m[32-63]
- 22:
$d
(indices over 7 are wrapped modulo 8) (G80 only) - 23:
$f
(indices over 1 are wrapped modulo 2) - 24:
$x
(indices over 15 are wrapped modulo 16) (G80 only)
Todo
figure out the pre-G80 register files
Attempts to read or write unknown register file are ignored. In case of reads, the destination register is left unmodified.
- Instructions:
Instruction Operands Opcode mov
[$c[CDST]] $<RFILE>[DST] $r[SRC1]
0x6a
mov
[$c[CDST]] $r[DST] $<RFILE>[SRC1]
0x6b
- Operation:
if opcode == 0x6a: $<RFILE>[DST] = $r[SRC1] else: $r[DST] = $<RFILE>[SRC1] if CDST < 4: $c[CDST].scalar = 0
Arithmetic operations: mul, min, max, abs, neg, add, sub, shr, sar¶
mul
performs a 16x16 multiplication with 32 bit result. shr
and
sar
do a bitwise shift right by given amount, with negative amounts
interpreted as left shift (and the shift amount limitted to -0x1f..0x1f
).
The other operations do what it says on the tin. abs
, min
, max
,
mul
, sar
treat the inputs as signed, shr
as unsigned, for others
it doesn’t matter.
The first source comes from a register selected by SRC1
, and the second
comes from either a register selected by mangled field SRC2S
or a 13-bit
signed immediate IMM
. In case of abs
and neg
, the second source
is unused, and the immediate versions are redundant (and in fact one set of
opcodes is used for mov to/from other register file instead).
Most of these operations have duplicate opcodes. The canonical one is the lowest one.
All of these operations set the full set of scalar condition codes.
- Instructions:
Instruction Operands Opcode mul
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x41, 0x51
min
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x48, 0x58
max
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x49, 0x59
abs
[$c[CDST]] $r[DST] $r[SRC1]
0x4a, 0x5a, 0x7a
neg
[$c[CDST]] $r[DST] $r[SRC1]
0x4b, 0x5b, 0x7b
add
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x4c, 0x5c
sub
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x4d, 0x5d
sar
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x4e
shr
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x5e
mul
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x61, 0x71
min
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x68, 0x78
max
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x69, 0x79
add
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x6c, 0x7c
sub
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x6d, 0x7d
sar
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x6e
shr
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x7e
- Operation:
s1 = sext($r[SRC1], 31) if opcode & 0x20: s2 = sext(IMM, 12) else: s2 = sext($r[SRC2], 31) if op == 'mul': res = sext(s1, 15) * sext(s2, 15) elif op == 'min': res = min(s1, s2) elif op == 'max': res = max(s1, s2) elif op == 'abs': res = abs(s1) elif op == 'neg': res = -s1 elif op == 'add': res = s1 + s2 elif op == 'sub': res = s1 - s2 elif op == 'shr' or op == 'sar': # shr/sar are unsigned/signed versions of the same insn if op == 'shr': s1 &= 0xffffffff # shift amount is 6-bit signed number shift = sext(s2, 5) # and -0x20 is invalid if shift == -0x20: shift = 0 # negative shifts mean a left shift if shift < 0: res = s1 << -shift else: # sign of s1 matters here res = s1 >> shift $r[DST] = res # build $c result cres = 0 if res & 1 << 31: cres |= 1 if res == 0: cres |= 2 if res & 1 << 19: cres |= 4 if (res ^ s1) & 1 << 20: cres |= 8 if res & 1 << 20: cres |= 0x10 if res & 1 << 21: cres |= 0x20 if variant == 'G80': if res & 1 << 19: cres |= 0x40 if res & 1 << 18: cres |= 0x80 if CDST < 4: $c[CDST].scalar = cres
Bit operations: bitop¶
Performs an arbitrary two-input bit operation on two registers,
selected by SRC1
and SRC2
. $c
output works, but only with
a subset of flags.
- Instructions:
Instruction Operands Opcode bitop
BITOP [$c[CDST]] $r[DST] $r[SRC1] $r[SRC2]
0x42
- Operation:
s1 = $r[SRC1] s2 = $r[SRC2] res = bitop(BITOP, s2, s1) & 0xffffffff $r[DST] = res # build $c result cres = 0 # bit 0 not set if res == 0: cres |= 2 if res & 1 << 19: cres |= 4 # bit 3 not set if res & 1 << 20: cres |= 0x10 if res & 1 << 21: cres |= 0x20 if variant == 'G80': if res & 1 << 19: cres |= 0x40 if res & 1 << 18: cres |= 0x80 if CDST < 4: $c[CDST].scalar = cres
Bit operations with immediate: and, or, xor¶
Performs a given bitwise operation on a register and 13-bit immediate. Like
for bitop, $c
output only works partially.
- Instructions:
Instruction Operands Opcode and
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x62
xor
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x63
or
[$c[CDST]] $r[DST] $r[SRC1] IMM
0x64
- Operation:
s1 = $r[SRC1] if op == 'and': res = s1 & IMM elif op == 'xor': res = s1 ^ IMM elif op == 'or': res = s1 | IMM $r[DST] = res # build $c result cres = 0 # bit 0 not set if res == 0: cres |= 2 if res & 1 << 19: cres |= 4 # bit 3 not set if res & 1 << 20: cres |= 0x10 if res & 1 << 21: cres |= 0x20 if variant == 'G80': if res & 1 << 19: cres |= 0x40 if res & 1 << 18: cres |= 0x80 if CDST < 4: $c[CDST].scalar = cres
Simple bytewise operations: bmin, bmax, babs, bneg, badd, bsub¶
Those perform the corresponding operation (minumum, maximum, absolute value,
negation, addition, substraction) in SIMD manner on 8-bit signed or unsigned
numbers from one or two sources. Source 1 is always a register selected by
SRC1
bitfield. Source 2, if it is used (ie. instruction is not babs
nor bneg
), is either a register selected by SRC2S
mangled bitfield,
or immediate taken from BIMM
bitfield.
Each of these instructions comes in signed and unsigned variants and both perform result clipping. Note that abs is rather uninteresting in its unsigned variant (it’s just the identity function), and so is neg (result is always 0 or clipped to 0.
These instruction have a $c
output, but it’s always set to all-0 if used.
Also note that babs
and bneg
have two redundant opcodes each: the bit
that normally selects immediate or register second source doesn’t apply
to them.
- Instructions:
Instruction Operands Opcode bmin s
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x08
bmax s
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x09
babs s
[$c[CDST]] $r[DST] $r[SRC1]
0x0a
bneg s
[$c[CDST]] $r[DST] $r[SRC1]
0x0b
badd s
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x0c
bsub s
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x0d
bmin u
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x18
bmax u
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x19
babs u
[$c[CDST]] $r[DST] $r[SRC1]
0x1a
bneg u
[$c[CDST]] $r[DST] $r[SRC1]
0x1b
badd u
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x1c
bsub u
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x1d
bmin s
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x28
bmax s
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x29
babs s
[$c[CDST]] $r[DST] $r[SRC1]
0x2a
bneg s
[$c[CDST]] $r[DST] $r[SRC1]
0x2b
badd s
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x2c
bsub s
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x2d
bmin u
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x38
bmax u
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x39
babs u
[$c[CDST]] $r[DST] $r[SRC1]
0x3a
bneg u
[$c[CDST]] $r[DST] $r[SRC1]
0x3b
badd u
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x3c
bsub u
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x3d
- Operation:
for idx in range(4): s1 = $r[SRC1][idx] if opcode & 0x20: s2 = BIMM else: s2 = $r[SRC2S][idx] if opcode & 0x10: # unsigned s1 &= 0xff s2 &= 0xff else: # signed s1 = sext(s1, 7) s2 = sext(s2, 7) if op == 'bmin': res = min(s1, s2) elif op == 'bmax': res = max(s1, s2) elif op == 'babs': res = abs(s1) elif op == 'bneg': res = -s1 elif op == 'badd': res = s1 + s2 elif op == 'bsub': res = s1 - s2 if opcode & 0x10: # unsigned: clip to 0..0xff if res < 0: res = 0 if res > 0xff: res = 0xff else: # signed: clip to -0x80..0x7f if res < -0x80: res = -0x80 if res > 0x7f: res = 0x7f $r[DST][idx] = res if CDST < 4: $c[CDST].scalar = 0
Bytewise bit operations: band, bor, bxor¶
Performs a given bitwise operation on a register and an 8-bit immediate
replicated 4 times. Or, intepreted differently, performs such operation on
every byte of a register idependently. $c
output is present, but always
outputs 0.
- Instructions:
Instruction Operands Opcode and
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x25
or
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x26
xor
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x27
- Operation:
for idx in range(4): if op == 'and': $r[DST][idx] = $r[SRC1][idx] & BIMM elif op == 'or': $r[DST][idx] = $r[SRC1][idx] | BIMM elif op == 'xor': $r[DST][idx] = $r[SRC1][idx] ^ BIMM if CDST < 4: $c[CDST].scalar = 0
Bytewise bit shift operations: bshr, bsar¶
Performs a bytewise SIMD right shift. Like the usual shift instruction, the shift amount is considered signed and negative amounts result in left shift. In this case, the shift amount is a 4-bit signed number. Operands are as in usual bytewise operations.
- Instructions:
Instruction Operands Opcode bsar
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x0e
bshr
[$c[CDST]] $r[DST] $r[SRC1] $r[SRC2S]
0x1e
bsar
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x2e
bshr
[$c[CDST]] $r[DST] $r[SRC1] BIMM
0x3e
- Operation:
for idx in range(4): s1 = $r[SRC1][idx] if opcode & 0x20: s2 = BIMM else: s2 = $r[SRC2S][idx] if opcode & 0x10: # unsigned s1 &= 0xff else: # signed s1 = sext(s1, 7) shift = sext(s2, 3) if shift < 0: res = s1 << -shift else: res = s1 >> shift $r[DST][idx] = res if CDST < 4: $c[CDST].scalar = 0
Bytewise multiplication: bmul¶
These instructions perform bytewise fractional multiplication: the inputs and
outputs are considered to be fixed-point numbers with 8 fractional bits
(unsigned version) or 7 fractional bits (signed version). The signedness of
both inputs and the output can be controlled independently (the signedness
of the output is controlled by the opcode, and of the inputs by instruction
word flags SIGN1
and SIGN2
). The results are clipped to the output
range. There are two rounding modes: round down and round to nearest with
ties rounded up.
The first source is always a register selected by SRC1
bitfield.
The second source can be a register selected by SRC2
bitfield, or 6-bit
immediate in BIMMMUL
bitfield padded with two zero bits on the right.
Note that besides proper 0xX1
opcodes, there are also 0xX2
bad
opcodes. In case of register-register ops, these opcodes
are just aliases of the sane ones, but for immediate opcodes, a colliding
bitfield is used.
The instructions have no $c
output capability.
- Instructions:
Instruction Operands Opcode bmul s
RND $r[DST] SIGN1 $r[SRC1] SIGN2 $r[SRC2]
0x01, 0x02
bmul u
RND $r[DST] SIGN1 $r[SRC1] SIGN2 $r[SRC2]
0x11, 0x12
bmul s
RND $r[DST] SIGN1 $r[SRC1] SIGN2 BIMMMUL
0x21
bmul u
RND $r[DST] SIGN1 $r[SRC1] SIGN2 BIMMMUL
0x31
bmul s
RND $r[DST] SIGN1 $r[SRC1] SIGN2 BIMMBAD
0x22
(bad opcode)bmul u
RND $r[DST] SIGN1 $r[SRC1] SIGN2 BIMMBAD
0x32
(bad opcode)- Operation:
for idx in range(4): # read inputs s1 = $r[SRC1][idx] if opcode & 0x20: if opcode & 2: s2 = BIMMBAD else: s2 = BIMMMUL << 2 else: s2 = $r[SRC2S][idx] # convert inputs to 8 fractional bits - unsigned inputs are already ok if SIGN1: ss1 = sext(ss1, 7) << 1 if SIGN2: ss2 = sext(ss2, 7) << 1 # multiply - the result has 16 fractional bits res = ss1 * ss2 if opcode & 0x10: # unsigned result # first, if round to nearest is selected, apply rounding correction if RND == 'rn': res += 0x80 # convert to 8 fractional bits res >>= 8 # clip if res < 0: res = 0 if res > 0xff: res = 0xff else: # signed result if RND == 'rn': res += 0x100 # convert to 7 fractional bits res >>= 9 # clip if res < -0x80: res = -0x80 if res > 0x7f: res = 0x7f $r[DST][idx] = res
Send immediate to vector unit: vec¶
This instruction takes two 9-bit immediate operands and sends them as factors
to the vector unit. The first immediate is used as factors 0 and 1, and the
second is used as factors 2 and 3. $vc
selection is sent as well.
- Instructions:
Instruction Operands Opcode vec
FACTOR1 FACTOR2 $vc[VCIDX] VCFLAG VCXFRM
0x24
- Operation:
s2v.factor[0] = s2v.factor[1] = FACTOR1 s2v.factor[2] = s2v.factor[3] = FACTOR2 s2v.vcsel.idx = VCIDX s2v.vcsel.flag = VCFLAG s2v.vcsel.xfrm = VCXFRM
Send mask to vector unit and shift: vecms¶
This instruction shifts a register right by 4 bits and uses the bits shifted
out as s2v mask 0 after expansion (each bit is replicated 4 times). The s2v
factors are derived from that mask and are not very useful. The right shift
is sign-filling. $vc
selection is sent as well.
- Instructions:
Instruction Operands Opcode vecms
$r[SRC1] $vc[VCIDX] VCFLAG VCXFRM
0x45
- Operation:
val = sext($r[SRC1], 31) $r[SRC1] = val >> 4 # the factors are made so that the mask derived from them will contain # each bit from the short mask repeated 4 times f0 = 0 f1 = 0 if val & 1: f0 |= 0x1e if val & 2: f0 |= 0x1e0 if val & 4: f1 |= 0x1e if val & 8: f1 |= 0x1e0 s2v.factor[0] = f0 s2v.factor[1] = f1 s2v.factor[2] = s2v.factor[3] = 0 s2v.vcsel.idx = VCIDX s2v.vcsel.flag = VCFLAG s2v.vcsel.xfrm = VCXFRM
Send bytes to vector unit: bvec¶
Treats a register as 4-byte vector, sends the bytes as s2v factors (treating
them as signed with 7 fractional bits). $vc
selection is sent as well.
If the s2v output is used as masks, this effectively takes mask 0 from source
bits 0-15 and mask 1 from source bits 16-31.
- Instructions:
Instruction Operands Opcode bvec
$r[SRC1] $vc[VCIDX] VCFLAG VCXFRM
0x0f
- Operation:
for idx in range(4): s2v.factor[idx] = sext($r[SRC1][idx], 7) << 1 s2v.vcsel.idx = VCIDX s2v.vcsel.flag = VCFLAG s2v.vcsel.xfrm = VCXFRM
Bytewise multiply, add, and send to vector unit: bvecmad, bvecmadsel¶
Figure out this one yourself. It sends s2v factors based on SIMD multiply & add, uses weird source mangling, and even weirder source 1 bitfields.
- Instructions:
Instruction Operands Opcode bvecmad
$r[SRC1] $r[SRC2]q $vc[VCIDX] VCFLAG VCXFRM
0x04
bvecmadsel
$r[SRC1] $r[SRC2]q $vc[VCIDX] VCFLAG VCXFRM
0x05
- Operation:
if SLCT== 4: adjust = $c[COND] >> 4 & 3 else: adjust = $c[COND] >> SLCT & 1 # SRC1 selects the pre-factor, which will be multiplied by source 3 if op == 'bvecmad': prefactor = $r[SRC1] >> 11 & 0xff elif op == 'bvecmadsel': prefactor = $r[SRC1] >> 11 & 0x7f s2a = $r[SRC2 | adjust] s2b = $r[SRC2 | 2 | adjust] for idx in range(4): # this time source is mangled by OR, not XOR - don't ask me if op == 'bvecmad' midx = idx elif op == 'bvecmadsel': midx = idx & 2 if SLCT == 2 and $c[COND] & 0x80: midx |= 1 # baseline (res will have 16 fractional bits, sources have 8) res = s2a[midx] << 8 # throw in the multiplication result res += prefactor * s2b[idx] # and rounding correction (for round to nearest, ties up) res += 0x40 # and round to 9 fractional bits s2v.factor[idx] = res >> 7 s2v.vcsel.idx = VCIDX s2v.vcsel.flag = VCFLAG s2v.vcsel.xfrm = VCXFRM