mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-04 22:49:13 +02:00
afuc: Convert to isaspec
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23949>
This commit is contained in:
parent
7376af0eef
commit
1046ebbb89
11 changed files with 1440 additions and 1435 deletions
|
|
@ -1,141 +1,150 @@
|
|||
; a6xx microcode
|
||||
; Version: 01000001
|
||||
|
||||
[01000001] ; nop
|
||||
[01000078] ; nop
|
||||
mov $01, 0x0830 ; CP_SQE_INSTR_BASE
|
||||
mov $02, 0x0002
|
||||
cwrite $01, [$00 + @REG_READ_ADDR], 0x0
|
||||
cwrite $02, [$00 + @REG_READ_DWORDS], 0x0
|
||||
mov $01, $regdata
|
||||
mov $02, $regdata
|
||||
add $01, $01, 0x0004
|
||||
addhi $02, $02, 0x0000
|
||||
mov $03, 0x0001
|
||||
cwrite $01, [$00 + @MEM_READ_ADDR], 0x0
|
||||
cwrite $02, [$00 + @MEM_READ_ADDR+0x1], 0x0
|
||||
cwrite $03, [$00 + @MEM_READ_DWORDS], 0x0
|
||||
rot $04, $memdata, 0x0008
|
||||
ushr $04, $04, 0x0006
|
||||
sub $04, $04, 0x0004
|
||||
add $01, $01, $04
|
||||
addhi $02, $02, 0x0000
|
||||
mov $rem, 0x0080
|
||||
cwrite $01, [$00 + @MEM_READ_ADDR], 0x0
|
||||
cwrite $02, [$00 + @MEM_READ_ADDR+0x1], 0x0
|
||||
cwrite $02, [$00 + @LOAD_STORE_HI], 0x0
|
||||
cwrite $rem, [$00 + @MEM_READ_DWORDS], 0x0
|
||||
cwrite $00, [$00 + @PACKET_TABLE_WRITE_ADDR], 0x0
|
||||
(rep)cwrite $memdata, [$00 + @PACKET_TABLE_WRITE], 0x0
|
||||
mov $02, 0x0883 ; CP_SCRATCH[0].REG
|
||||
mov $03, 0xbeef
|
||||
mov $04, 0xdead << 16
|
||||
or $03, $03, $04
|
||||
cwrite $02, [$00 + @REG_WRITE_ADDR], 0x0
|
||||
cwrite $03, [$00 + @REG_WRITE], 0x0
|
||||
waitin
|
||||
mov $01, $data
|
||||
[01000001]
|
||||
[01000078]
|
||||
mov $01, 0x830 ; CP_SQE_INSTR_BASE
|
||||
mov $02, 0x2
|
||||
cwrite $01, [$00 + @REG_READ_ADDR], 0x0
|
||||
cwrite $02, [$00 + @REG_READ_DWORDS], 0x0
|
||||
mov $01, $regdata
|
||||
mov $02, $regdata
|
||||
add $01, $01, 0x4
|
||||
addhi $02, $02, 0x0
|
||||
mov $03, 0x1
|
||||
cwrite $01, [$00 + @MEM_READ_ADDR], 0x0
|
||||
cwrite $02, [$00 + @MEM_READ_ADDR+0x1], 0x0
|
||||
cwrite $03, [$00 + @MEM_READ_DWORDS], 0x0
|
||||
rot $04, $memdata, 0x8
|
||||
ushr $04, $04, 0x6
|
||||
sub $04, $04, 0x4
|
||||
add $01, $01, $04
|
||||
addhi $02, $02, 0x0
|
||||
mov $rem, 0x80
|
||||
cwrite $01, [$00 + @MEM_READ_ADDR], 0x0
|
||||
cwrite $02, [$00 + @MEM_READ_ADDR+0x1], 0x0
|
||||
cwrite $02, [$00 + @LOAD_STORE_HI], 0x0
|
||||
cwrite $rem, [$00 + @MEM_READ_DWORDS], 0x0
|
||||
cwrite $00, [$00 + @PACKET_TABLE_WRITE_ADDR], 0x0
|
||||
(rep)cwrite $memdata, [$00 + @PACKET_TABLE_WRITE], 0x0
|
||||
mov $02, 0x883 ; CP_SCRATCH[0].REG
|
||||
mov $03, 0xbeef
|
||||
mov $04, 0xdead << 16
|
||||
or $03, $03, $04
|
||||
cwrite $02, [$00 + @REG_WRITE_ADDR], 0x0
|
||||
cwrite $03, [$00 + @REG_WRITE], 0x0
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
CP_ME_INIT:
|
||||
mov $02, 0x0002
|
||||
waitin
|
||||
mov $01, $data
|
||||
mov $02, 0x2
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
CP_MEM_WRITE:
|
||||
mov $addr, 0x00a0 << 24 ; |NRT_ADDR
|
||||
mov $02, 0x0004
|
||||
(xmov1)add $data, $02, $data
|
||||
mov $addr, 0xa204 << 16 ; |NRT_DATA
|
||||
(rep)(xmov3)mov $data, $data
|
||||
waitin
|
||||
mov $01, $data
|
||||
mov $addr, 0xa0 << 24 ; |NRT_ADDR
|
||||
mov $02, 0x4
|
||||
(xmov1)add $data, $02, $data
|
||||
mov $addr, 0xa204 << 16 ; |NRT_DATA
|
||||
(rep)(xmov3)mov $data, $data
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
CP_SCRATCH_WRITE:
|
||||
mov $02, 0x00ff
|
||||
(rep)cwrite $data, [$02 + 0x001], 0x4
|
||||
waitin
|
||||
mov $01, $data
|
||||
mov $02, 0xff
|
||||
(rep)cwrite $data, [$02 + @RB_RPTR], 0x4
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
CP_SET_SECURE_MODE:
|
||||
mov $02, $data
|
||||
setsecure $02, #l000
|
||||
l001: jump #l001
|
||||
nop
|
||||
l000: waitin
|
||||
mov $01, $data
|
||||
fxn00:
|
||||
l004: cmp $04, $02, $03
|
||||
breq $04, b0, #l002
|
||||
brne $04, b1, #l003
|
||||
breq $04, b2, #l004
|
||||
sub $03, $03, $02
|
||||
l003: jump #l004
|
||||
sub $02, $02, $03
|
||||
l002: ret
|
||||
nop
|
||||
mov $02, $data
|
||||
setsecure $02, #l52
|
||||
l50:
|
||||
jump #l50
|
||||
nop
|
||||
l52:
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
fxn54:
|
||||
l54:
|
||||
cmp $04, $02, $03
|
||||
breq $04, b0, #l61
|
||||
brne $04, b1, #l59
|
||||
breq $04, b2, #l54
|
||||
sub $03, $03, $02
|
||||
l59:
|
||||
jump #l54
|
||||
sub $02, $02, $03
|
||||
l61:
|
||||
ret
|
||||
nop
|
||||
|
||||
CP_REG_RMW:
|
||||
cwrite $data, [$00 + @REG_READ_ADDR], 0x0
|
||||
add $02, $regdata, 0x0042
|
||||
addhi $03, $00, $regdata
|
||||
sub $02, $02, $regdata
|
||||
call #fxn00
|
||||
subhi $03, $03, $regdata
|
||||
and $02, $02, $regdata
|
||||
or $02, $02, 0x0001
|
||||
xor $02, $02, 0x0001
|
||||
not $02, $02
|
||||
shl $02, $02, $regdata
|
||||
ushr $02, $02, $regdata
|
||||
ishr $02, $02, $regdata
|
||||
rot $02, $02, $regdata
|
||||
min $02, $02, $regdata
|
||||
max $02, $02, $regdata
|
||||
mul8 $02, $02, $regdata
|
||||
msb $02, $02
|
||||
mov $usraddr, $data
|
||||
mov $data, $02
|
||||
waitin
|
||||
mov $01, $data
|
||||
cwrite $data, [$00 + @REG_READ_ADDR], 0x0
|
||||
add $02, $regdata, 0x42
|
||||
addhi $03, $00, $regdata
|
||||
sub $02, $02, $regdata
|
||||
call #fxn54
|
||||
subhi $03, $03, $regdata
|
||||
and $02, $02, $regdata
|
||||
or $02, $02, 0x1
|
||||
xor $02, $02, 0x1
|
||||
not $02, $02
|
||||
shl $02, $02, $regdata
|
||||
ushr $02, $02, $regdata
|
||||
ishr $02, $02, $regdata
|
||||
rot $02, $02, $regdata
|
||||
min $02, $02, $regdata
|
||||
max $02, $02, $regdata
|
||||
mul8 $02, $02, $regdata
|
||||
msb $02, $02
|
||||
mov $usraddr, $data
|
||||
mov $data, $02
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
CP_MEMCPY:
|
||||
mov $02, $data
|
||||
mov $03, $data
|
||||
mov $04, $data
|
||||
mov $05, $data
|
||||
mov $06, $data
|
||||
l006: breq $06, 0x0, #l005
|
||||
cwrite $03, [$00 + @LOAD_STORE_HI], 0x0
|
||||
load $07, [$02 + 0x004], 0x4
|
||||
cwrite $05, [$00 + @LOAD_STORE_HI], 0x0
|
||||
jump #l006
|
||||
store $07, [$04 + 0x004], 0x4
|
||||
l005: waitin
|
||||
mov $01, $data
|
||||
mov $02, $data
|
||||
mov $03, $data
|
||||
mov $04, $data
|
||||
mov $05, $data
|
||||
mov $06, $data
|
||||
l90:
|
||||
breq $06, 0x0, #l96
|
||||
cwrite $03, [$00 + @LOAD_STORE_HI], 0x0
|
||||
load $07, [$02 + 0x4], 0x4
|
||||
cwrite $05, [$00 + @LOAD_STORE_HI], 0x0
|
||||
jump #l90
|
||||
store $07, [$04 + 0x4], 0x4
|
||||
l96:
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
CP_MEM_TO_MEM:
|
||||
cwrite $data, [$00 + @MEM_READ_ADDR], 0x0
|
||||
cwrite $data, [$00 + @MEM_READ_ADDR+0x1], 0x0
|
||||
mov $02, $data
|
||||
cwrite $data, [$00 + @LOAD_STORE_HI], 0x0
|
||||
mov $rem, $data
|
||||
cwrite $rem, [$00 + @MEM_READ_DWORDS], 0x0
|
||||
(rep)store $memdata, [$02 + 0x004], 0x4
|
||||
waitin
|
||||
mov $01, $data
|
||||
cwrite $data, [$00 + @MEM_READ_ADDR], 0x0
|
||||
cwrite $data, [$00 + @MEM_READ_ADDR+0x1], 0x0
|
||||
mov $02, $data
|
||||
cwrite $data, [$00 + @LOAD_STORE_HI], 0x0
|
||||
mov $rem, $data
|
||||
cwrite $rem, [$00 + @MEM_READ_DWORDS], 0x0
|
||||
(rep)store $memdata, [$02 + 0x4], 0x4
|
||||
waitin
|
||||
mov $01, $data
|
||||
|
||||
IN_PREEMPT:
|
||||
cread $02, [$00 + 0x101], 0x0
|
||||
brne $02, 0x1, #l007
|
||||
nop
|
||||
preemptleave #l001
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
waitin
|
||||
mov $01, $data
|
||||
l007: iret
|
||||
nop
|
||||
cread $02, [$00 + 0x101], 0x0
|
||||
brne $02, 0x1, #l116
|
||||
nop
|
||||
preemptleave #l50
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
waitin
|
||||
mov $01, $data
|
||||
l116:
|
||||
iret
|
||||
nop
|
||||
|
||||
UNKN0:
|
||||
UNKN1:
|
||||
|
|
@ -257,133 +266,133 @@ UNKN124:
|
|||
UNKN125:
|
||||
UNKN126:
|
||||
UNKN127:
|
||||
waitin
|
||||
mov $01, $data
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[0000006b] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[0000003f] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000025] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000022] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[0000002c] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000030] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000062] ; nop
|
||||
[00000076] ; nop
|
||||
[00000055] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
[00000076] ; nop
|
||||
waitin
|
||||
mov $01, $data
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[0000006b]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[0000003f]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000025]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000022]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[0000002c]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000030]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000062]
|
||||
[00000076]
|
||||
[00000055]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
[00000076]
|
||||
|
|
|
|||
|
|
@ -38,38 +38,36 @@ if flag set, copy cmdstream bo contents into RB instead of IB'ing to it from
|
|||
RB.
|
||||
*/
|
||||
|
||||
/* The opcode is encoded variable length. Opcodes less than 0x30
|
||||
* are encoded as 5 bits followed by (rep) flag. Opcodes >= 0x30
|
||||
* (ie. top two bits are '11' are encoded as 6 bits. See get_opc()
|
||||
*/
|
||||
typedef enum {
|
||||
OPC_NOP = 0x00,
|
||||
OPC_NOP,
|
||||
|
||||
OPC_ADD = 0x01, /* add immediate */
|
||||
OPC_ADDHI = 0x02, /* add immediate (hi 32b of 64b) */
|
||||
OPC_SUB = 0x03, /* subtract immediate */
|
||||
OPC_SUBHI = 0x04, /* subtract immediate (hi 32b of 64b) */
|
||||
OPC_AND = 0x05, /* AND immediate */
|
||||
OPC_OR = 0x06, /* OR immediate */
|
||||
OPC_XOR = 0x07, /* XOR immediate */
|
||||
OPC_NOT = 0x08, /* bitwise not of immed (src1 ignored) */
|
||||
OPC_SHL = 0x09, /* shift-left immediate */
|
||||
OPC_USHR = 0x0a, /* unsigned shift right by immediate */
|
||||
OPC_ISHR = 0x0b, /* signed shift right by immediate */
|
||||
OPC_ROT = 0x0c, /* rotate left (left shift with wrap-around) */
|
||||
OPC_MUL8 = 0x0d, /* 8bit multiply by immediate */
|
||||
OPC_MIN = 0x0e,
|
||||
OPC_MAX = 0x0f,
|
||||
OPC_CMP = 0x10, /* compare src to immed */
|
||||
OPC_MOVI = 0x11, /* move immediate */
|
||||
#define ALU(name) \
|
||||
OPC_##name, \
|
||||
OPC_##name##I,
|
||||
ALU(ADD) /* add immediate */
|
||||
ALU(ADDHI) /* add immediate (hi 32b of 64b) */
|
||||
ALU(SUB) /* subtract immediate */
|
||||
ALU(SUBHI) /* subtract immediate (hi 32b of 64b) */
|
||||
ALU(AND) /* AND immediate */
|
||||
ALU(OR) /* OR immediate */
|
||||
ALU(XOR) /* XOR immediate */
|
||||
ALU(NOT) /* bitwise not of immed (src1 ignored) */
|
||||
ALU(SHL) /* shift-left immediate */
|
||||
ALU(USHR) /* unsigned shift right by immediate */
|
||||
ALU(ISHR) /* signed shift right by immediate */
|
||||
ALU(ROT) /* rotate left (left shift with wrap-around) */
|
||||
ALU(MUL8) /* 8bit multiply by immediate */
|
||||
ALU(MIN)
|
||||
ALU(MAX)
|
||||
ALU(CMP) /* compare src to immed */
|
||||
OPC_MOVI, /* move immediate */
|
||||
#undef ALU
|
||||
|
||||
/* Return the most-significant bit of src2, or 0 if src2 == 0 (the
|
||||
* same as if src2 == 1). src1 is ignored. Note that this overlaps
|
||||
* with STORE6, so it can only be used with the two-source encoding.
|
||||
* with STORE, so it can only be used with the two-source encoding.
|
||||
*/
|
||||
OPC_MSB = 0x14,
|
||||
|
||||
OPC_ALU = 0x13, /* ALU instruction with two src registers */
|
||||
OPC_MSB,
|
||||
|
||||
/* These seem something to do with setting some external state..
|
||||
* doesn't seem to map *directly* to registers, but I guess that
|
||||
|
|
@ -90,26 +88,31 @@ typedef enum {
|
|||
* 0x0b22->0x0b24 (IB2). Presumably $05 ends up w/ different value
|
||||
* for RB->IB1 vs IB1->IB2.
|
||||
*/
|
||||
OPC_CWRITE5 = 0x15,
|
||||
OPC_CREAD5 = 0x16,
|
||||
OPC_CWRITE,
|
||||
OPC_CREAD,
|
||||
|
||||
/* A6xx shuffled around the cwrite/cread opcodes and added new opcodes
|
||||
* that let you read/write directly to memory (and bypass the IOMMU?).
|
||||
/* A6xx added new opcodes that let you read/write directly to memory (and
|
||||
* bypass the IOMMU?).
|
||||
*/
|
||||
OPC_STORE6 = 0x14,
|
||||
OPC_CWRITE6 = 0x15,
|
||||
OPC_LOAD6 = 0x16,
|
||||
OPC_CREAD6 = 0x17,
|
||||
OPC_STORE,
|
||||
OPC_LOAD,
|
||||
|
||||
OPC_BRNEI = 0x30, /* relative branch (if $src != immed) */
|
||||
OPC_BREQI = 0x31, /* relative branch (if $src == immed) */
|
||||
OPC_BRNEB = 0x32, /* relative branch (if bit not set) */
|
||||
OPC_BREQB = 0x33, /* relative branch (if bit is set) */
|
||||
OPC_RET = 0x34, /* return */
|
||||
OPC_CALL = 0x35, /* "function" call */
|
||||
OPC_WIN = 0x36, /* wait for input (ie. wait for WPTR to advance) */
|
||||
OPC_PREEMPTLEAVE6 = 0x38, /* try to leave preemption */
|
||||
OPC_SETSECURE = 0x3b, /* switch secure mode on/off */
|
||||
OPC_BRNEI, /* relative branch (if $src != immed) */
|
||||
OPC_BREQI, /* relative branch (if $src == immed) */
|
||||
OPC_BRNEB, /* relative branch (if bit not set) */
|
||||
OPC_BREQB, /* relative branch (if bit is set) */
|
||||
OPC_RET, /* return */
|
||||
OPC_IRET, /* return from preemption interrupt handler */
|
||||
OPC_CALL, /* "function" call */
|
||||
OPC_WAITIN, /* wait for input (ie. wait for WPTR to advance) */
|
||||
OPC_PREEMPTLEAVE, /* try to leave preemption */
|
||||
OPC_SETSECURE, /* switch secure mode on/off */
|
||||
|
||||
/* pseudo-opcodes without an actual encoding */
|
||||
OPC_BREQ,
|
||||
OPC_BRNE,
|
||||
OPC_JUMP,
|
||||
OPC_RAW_LITERAL,
|
||||
} afuc_opc;
|
||||
|
||||
/**
|
||||
|
|
@ -141,97 +144,27 @@ typedef enum {
|
|||
REG_DATA = 0x1f,
|
||||
} afuc_reg;
|
||||
|
||||
typedef union PACKED {
|
||||
/* addi, subi, andi, ori, xori, etc: */
|
||||
struct PACKED {
|
||||
uint32_t uimm : 16;
|
||||
uint32_t dst : 5;
|
||||
uint32_t src : 5;
|
||||
uint32_t hdr : 6;
|
||||
} alui;
|
||||
struct PACKED {
|
||||
uint32_t uimm : 16;
|
||||
uint32_t dst : 5;
|
||||
uint32_t shift : 5;
|
||||
uint32_t hdr : 6;
|
||||
} movi;
|
||||
struct PACKED {
|
||||
uint32_t alu : 5;
|
||||
uint32_t pad : 4;
|
||||
uint32_t xmov : 2; /* execute eXtra mov's based on $rem */
|
||||
uint32_t dst : 5;
|
||||
uint32_t src2 : 5;
|
||||
uint32_t src1 : 5;
|
||||
uint32_t hdr : 6;
|
||||
} alu;
|
||||
struct PACKED {
|
||||
uint32_t uimm : 12;
|
||||
/* TODO this needs to be confirmed:
|
||||
*
|
||||
* flags:
|
||||
* 0x4 - post-increment src2 by uimm (need to confirm this is also
|
||||
* true for load/cread). TBD whether, when used in conjunction
|
||||
* with @LOAD_STORE_HI, 32b rollover works properly.
|
||||
*
|
||||
* other values tbd, also need to confirm if different bits can be
|
||||
* set together (I don't see examples of this in existing fw)
|
||||
*/
|
||||
uint32_t flags : 4;
|
||||
uint32_t src1 : 5; /* dst (cread) or src (cwrite) register */
|
||||
uint32_t src2 : 5; /* read or write address is src2+uimm */
|
||||
uint32_t hdr : 6;
|
||||
} control;
|
||||
struct PACKED {
|
||||
int32_t ioff : 16; /* relative offset */
|
||||
uint32_t bit_or_imm : 5;
|
||||
uint32_t src : 5;
|
||||
uint32_t hdr : 6;
|
||||
} br;
|
||||
struct PACKED {
|
||||
uint32_t uoff : 26; /* absolute (unsigned) offset */
|
||||
uint32_t hdr : 6;
|
||||
} call;
|
||||
struct PACKED {
|
||||
uint32_t pad : 25;
|
||||
uint32_t interrupt : 1; /* return from ctxt-switch interrupt handler */
|
||||
uint32_t hdr : 6;
|
||||
} ret;
|
||||
struct PACKED {
|
||||
uint32_t pad : 26;
|
||||
uint32_t hdr : 6;
|
||||
} waitin;
|
||||
struct PACKED {
|
||||
uint32_t pad : 26;
|
||||
uint32_t opc_r : 6;
|
||||
};
|
||||
struct afuc_instr {
|
||||
afuc_opc opc;
|
||||
|
||||
} afuc_instr;
|
||||
uint8_t dst;
|
||||
uint8_t src1;
|
||||
uint8_t src2;
|
||||
uint32_t immed;
|
||||
uint8_t shift;
|
||||
uint8_t bit;
|
||||
uint8_t xmov;
|
||||
uint32_t literal;
|
||||
int offset;
|
||||
const char *label;
|
||||
|
||||
static inline void
|
||||
afuc_get_opc(afuc_instr *ai, afuc_opc *opc, bool *rep)
|
||||
{
|
||||
if (ai->opc_r < 0x30) {
|
||||
*opc = ai->opc_r >> 1;
|
||||
*rep = ai->opc_r & 0x1;
|
||||
} else {
|
||||
*opc = ai->opc_r;
|
||||
*rep = false;
|
||||
}
|
||||
}
|
||||
bool has_immed : 1;
|
||||
bool has_shift : 1;
|
||||
bool has_bit : 1;
|
||||
bool is_literal : 1;
|
||||
bool rep : 1;
|
||||
};
|
||||
|
||||
static inline void
|
||||
afuc_set_opc(afuc_instr *ai, afuc_opc opc, bool rep)
|
||||
{
|
||||
if (opc < 0x30) {
|
||||
ai->opc_r = opc << 1;
|
||||
ai->opc_r |= !!rep;
|
||||
} else {
|
||||
ai->opc_r = opc;
|
||||
}
|
||||
}
|
||||
|
||||
void print_src(unsigned reg);
|
||||
void print_dst(unsigned reg);
|
||||
void print_control_reg(uint32_t id);
|
||||
void print_pipe_reg(uint32_t id);
|
||||
|
||||
|
|
|
|||
632
src/freedreno/afuc/afuc.xml
Normal file
632
src/freedreno/afuc/afuc.xml
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright © 2020 Google, Inc.
|
||||
Copyright © 2023 Valve Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-->
|
||||
|
||||
<isa>
|
||||
|
||||
<bitset name="#instruction" size="32">
|
||||
<doc>
|
||||
Encoding of an afuc instruction. All instructions are 32b.
|
||||
</doc>
|
||||
<encode type="struct afuc_instr *" case-prefix="OPC_">
|
||||
<map name="XMOV">src->xmov</map>
|
||||
<map name="DST">src->dst</map>
|
||||
<map name="SRC1">src->src1</map>
|
||||
<map name="SRC2">src->src2</map>
|
||||
<map name="IMMED">src->immed</map>
|
||||
<!-- RIMMED is an alias of IMMED for immediates that might be a GPU register -->
|
||||
<map name="RIMMED">src->immed</map>
|
||||
</encode>
|
||||
<decode/>
|
||||
</bitset>
|
||||
|
||||
<enum name="#specialsrc">
|
||||
<doc>special registers for operands used as a source</doc>
|
||||
<value val="0x1c" display="$rem"/>
|
||||
<value val="0x1d" display="$memdata"/>
|
||||
<value val="0x1e" display="$regdata"/>
|
||||
<value val="0x1f" display="$data"/>
|
||||
</enum>
|
||||
|
||||
<enum name="#specialdst">
|
||||
<doc>special registers for operands used as a destination</doc>
|
||||
<value val="0x1c" display="$rem"/>
|
||||
<value val="0x1d" display="$addr"/>
|
||||
<value val="0x1e" display="$usraddr"/>
|
||||
<value val="0x1f" display="$data"/>
|
||||
</enum>
|
||||
|
||||
<!-- TODO add support for padding integers so this doesn't have to be an enum -->
|
||||
<enum name="#reg">
|
||||
<value val="0x00" display="$00"/>
|
||||
<value val="0x01" display="$01"/>
|
||||
<value val="0x02" display="$02"/>
|
||||
<value val="0x03" display="$03"/>
|
||||
<value val="0x04" display="$04"/>
|
||||
<value val="0x05" display="$05"/>
|
||||
<value val="0x06" display="$06"/>
|
||||
<value val="0x07" display="$07"/>
|
||||
<value val="0x08" display="$08"/>
|
||||
<value val="0x09" display="$09"/>
|
||||
<value val="0x0a" display="$0a"/>
|
||||
<value val="0x0b" display="$0b"/>
|
||||
<value val="0x0c" display="$0c"/>
|
||||
<value val="0x0d" display="$0d"/>
|
||||
<value val="0x0e" display="$0e"/>
|
||||
<value val="0x0f" display="$0f"/>
|
||||
<value val="0x10" display="$10"/>
|
||||
<value val="0x11" display="$11"/>
|
||||
<value val="0x12" display="$12"/>
|
||||
<value val="0x13" display="$13"/>
|
||||
<value val="0x14" display="$14"/>
|
||||
<value val="0x15" display="$15"/>
|
||||
<value val="0x16" display="$16"/>
|
||||
<value val="0x17" display="$17"/>
|
||||
<value val="0x18" display="$18"/>
|
||||
<value val="0x19" display="$19"/>
|
||||
<value val="0x1a" display="$1a"/>
|
||||
<value val="0x1b" display="$1b"/>
|
||||
</enum>
|
||||
|
||||
<expr name="#reg-special">
|
||||
{SPECIALREG} >= 0x1c
|
||||
</expr>
|
||||
|
||||
<bitset name="#src" size="5">
|
||||
<override expr="#reg-special">
|
||||
<display>
|
||||
{SPECIALREG}
|
||||
</display>
|
||||
<field name="SPECIALREG" low="0" high="4" type="#specialsrc"/>
|
||||
</override>
|
||||
<display>
|
||||
{REG}
|
||||
</display>
|
||||
<field name="REG" low="0" high="4" type="#reg"/>
|
||||
|
||||
<encode type="uint8_t">
|
||||
<map name="REG">src</map>
|
||||
<map name="SPECIALREG">src</map>
|
||||
</encode>
|
||||
<decode/>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#dst" size="5">
|
||||
<override expr="#reg-special">
|
||||
<display>
|
||||
{SPECIALREG}
|
||||
</display>
|
||||
<field name="SPECIALREG" low="0" high="4" type="#specialdst"/>
|
||||
</override>
|
||||
<display>
|
||||
{REG}
|
||||
</display>
|
||||
<field name="REG" low="0" high="4" type="#reg"/>
|
||||
|
||||
<encode type="uint8_t">
|
||||
<map name="REG">src</map>
|
||||
<map name="SPECIALREG">src</map>
|
||||
</encode>
|
||||
<decode/>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#instruction-rep" extends="#instruction">
|
||||
<field name="REP" pos="26" type="bool" display="(rep)"/>
|
||||
|
||||
<encode>
|
||||
<map name="REP">src->rep</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<enum name="#xmov">
|
||||
<doc>Execute eXtra mov's based on $rem</doc>
|
||||
<value val="0" display=""/>
|
||||
<value val="1" display="(xmov1)"/>
|
||||
<value val="2" display="(xmov2)"/>
|
||||
<value val="3" display="(xmov3)"/>
|
||||
</enum>
|
||||
|
||||
<bitset name="#alu-2src" extends="#instruction-rep">
|
||||
<display>
|
||||
{REP}{XMOV}{NAME} {DST}, {SRC1}, {SRC2}
|
||||
</display>
|
||||
|
||||
<pattern low="5" high="8">xxxx</pattern>
|
||||
<field name="XMOV" low="9" high="10" type="#xmov"/>
|
||||
<field name="DST" low="11" high="15" type="#dst"/>
|
||||
<field name="SRC2" low="16" high="20" type="#src"/>
|
||||
<field name="SRC1" low="21" high="25" type="#src"/>
|
||||
<pattern low="27" high="31">10011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#alu-1src" extends="#instruction-rep">
|
||||
<display>
|
||||
{REP}{XMOV}{NAME} {DST}, {SRC1}
|
||||
</display>
|
||||
|
||||
<pattern low="5" high="8">xxxx</pattern>
|
||||
<field name="XMOV" low="9" high="10" type="#xmov"/>
|
||||
<field name="DST" low="11" high="15" type="#dst"/>
|
||||
<field name="SRC1" low="16" high="20" type="#src"/>
|
||||
<pattern low="21" high="25">xxxxx</pattern> <!-- SRC1 unused -->
|
||||
<pattern low="27" high="31">10011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#alu-2src-immed" extends="#instruction-rep">
|
||||
<display>
|
||||
{REP}{NAME} {DST}, {SRC1}, 0x{RIMMED}
|
||||
</display>
|
||||
|
||||
<field name="RIMMED" low="0" high="15" type="hex"/>
|
||||
<field name="DST" low="16" high="20" type="#dst"/>
|
||||
<field name="SRC1" low="21" high="25" type="#src"/>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#alu-1src-immed" extends="#instruction-rep">
|
||||
<display>
|
||||
{REP}{NAME} {DST}, 0x{IMMED}
|
||||
</display>
|
||||
|
||||
<field name="IMMED" low="0" high="15" type="hex"/>
|
||||
<field name="DST" low="16" high="20" type="#dst"/>
|
||||
<pattern low="21" high="25">xxxxx</pattern> <!-- SRC1 unused -->
|
||||
</bitset>
|
||||
|
||||
<bitset name="add" extends="#alu-2src">
|
||||
<doc>add and write carry flag</doc>
|
||||
<pattern low="0" high="4">00001</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="addi" displayname="add" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00001</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="addhi" extends="#alu-2src">
|
||||
<doc>Perform high 32 bits of 64-bit addition, using carry flag written by add</doc>
|
||||
<pattern low="0" high="4">00010</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="addhii" displayname="addhi" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00010</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="sub" extends="#alu-2src">
|
||||
<doc>subtract and write carry flag</doc>
|
||||
<pattern low="0" high="4">00011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="subi" displayname="sub" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="subhi" extends="#alu-2src">
|
||||
<doc>Perform high 32 bits of 64-bit subtraction, using carry flag written by sub</doc>
|
||||
<pattern low="0" high="4">00100</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="subhii" displayname="subhi" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00100</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="and" extends="#alu-2src">
|
||||
<pattern low="0" high="4">00101</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="andi" displayname="and" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00101</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="or" extends="#alu-2src">
|
||||
<!-- mov of a register is encoded as or with $00 -->
|
||||
<override>
|
||||
<expr>{SRC1} == 0</expr>
|
||||
<display>
|
||||
{REP}{XMOV}mov {DST}, {SRC2}
|
||||
</display>
|
||||
</override>
|
||||
<pattern low="0" high="4">00110</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="ori" displayname="or" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00110</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="xor" extends="#alu-2src">
|
||||
<pattern low="0" high="4">00111</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="xori" displayname="xor" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">00111</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="not" extends="#alu-1src">
|
||||
<pattern low="0" high="4">01000</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="noti" displayname="not" extends="#alu-1src-immed">
|
||||
<pattern low="27" high="31">01000</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="shl" extends="#alu-2src">
|
||||
<pattern low="0" high="4">01001</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="shli" displayname="shl" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01001</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="ushr" extends="#alu-2src">
|
||||
<doc>0-extending right shift</doc>
|
||||
<pattern low="0" high="4">01010</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="ushri" displayname="ushr" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01010</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="ishr" extends="#alu-2src">
|
||||
<doc>sign-extending right shift</doc>
|
||||
<pattern low="0" high="4">01011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="ishri" displayname="ishr" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="rot" extends="#alu-2src">
|
||||
<doc>Rotate left (left shift with wraparound)</doc>
|
||||
<pattern low="0" high="4">01100</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="roti" displayname="rot" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01100</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="mul8" extends="#alu-2src">
|
||||
<doc>Multiply low 8 bits of each source to produce a 16-bit result</doc>
|
||||
<pattern low="0" high="4">01101</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="mul8i" displayname="mul8" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01101</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="min" extends="#alu-2src">
|
||||
<doc>Unsigned minimum</doc>
|
||||
<pattern low="0" high="4">01110</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="mini" displayname="min" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01110</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="max" extends="#alu-2src">
|
||||
<doc>Unsigned maximum</doc>
|
||||
<pattern low="0" high="4">01111</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="maxi" displayname="max" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">01111</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="cmp" extends="#alu-2src">
|
||||
<doc>
|
||||
Compare two sources and produce a bitfield:
|
||||
- 0x00 if src1 > src2
|
||||
- 0x2b if src1 == src2
|
||||
- 0x1e if src1 < src2
|
||||
Often a "branch on bit set/unset" instruction is used on the
|
||||
result to implement a compare-and-branch macro.
|
||||
</doc>
|
||||
<pattern low="0" high="4">10000</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="cmpi" displayname="cmp" extends="#alu-2src-immed">
|
||||
<pattern low="27" high="31">10000</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="msb" extends="#alu-1src">
|
||||
<doc>Return the most-significant bit of src2, or 0 if src2 == 0</doc>
|
||||
<pattern low="0" high="4">10100</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="movi" extends="#instruction-rep">
|
||||
<doc>Special move-immediate instruction with a shift</doc>
|
||||
<override>
|
||||
<expr>{SHIFT} == 0</expr>
|
||||
<display>
|
||||
{REP}mov {DST}, 0x{RIMMED}
|
||||
</display>
|
||||
</override>
|
||||
<display>
|
||||
{REP}mov {DST}, 0x{RIMMED} << {SHIFT}
|
||||
</display>
|
||||
|
||||
<field name="RIMMED" low="0" high="15" type="hex"/>
|
||||
<field name="DST" low="16" high="20" type="#dst"/>
|
||||
<field name="SHIFT" low="21" high="25" type="uint"/>
|
||||
<pattern low="27" high="31">10001</pattern>
|
||||
|
||||
<encode>
|
||||
<map name="SHIFT">src->shift</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#control" extends="#instruction-rep">
|
||||
<field name="FLAGS" low="12" high="15" type="hex"/>
|
||||
<field name="OFFSET" low="21" high="25" type="#src"/>
|
||||
|
||||
<encode>
|
||||
<map name="FLAGS">src->bit</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="store" extends="#control">
|
||||
<gen min="6"/>
|
||||
<display>
|
||||
{REP}store {SRC}, [{OFFSET} + 0x{IMMED}], 0x{FLAGS}
|
||||
</display>
|
||||
|
||||
<doc>
|
||||
Store to memory directly. Mainly used by preemption to avoid
|
||||
disturbing FIFO state before it is saved and after it is
|
||||
restored.
|
||||
</doc>
|
||||
|
||||
<field name="IMMED" low="0" high="11" type="hex"/>
|
||||
<field name="SRC" low="16" high="20" type="#src"/>
|
||||
<pattern low="27" high="31">10100</pattern>
|
||||
|
||||
<encode>
|
||||
<map name="SRC">src->src1</map>
|
||||
<map name="OFFSET">src->src2</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="cwrite" extends="#control">
|
||||
<doc>Write to a control register.</doc>
|
||||
<display>
|
||||
{REP}cwrite {SRC}, [{OFFSET} + {CONTROLREG}], 0x{FLAGS}
|
||||
</display>
|
||||
|
||||
<field name="CONTROLREG" low="0" high="11" type="custom"/>
|
||||
<field name="SRC" low="16" high="20" type="#src"/>
|
||||
<pattern low="27" high="31">10101</pattern>
|
||||
|
||||
<encode>
|
||||
<map name="SRC">src->src1</map>
|
||||
<map name="OFFSET">src->src2</map>
|
||||
<map name="CONTROLREG">src->immed</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="load" extends="#control">
|
||||
<gen min="6"/>
|
||||
<doc>
|
||||
Load from memory directly. Mainly used by preemption to avoid
|
||||
disturbing FIFO state before it is saved and after it is
|
||||
restored.
|
||||
</doc>
|
||||
<display>
|
||||
{REP}load {DST}, [{OFFSET} + 0x{IMMED}], 0x{FLAGS}
|
||||
</display>
|
||||
|
||||
<field name="IMMED" low="0" high="11" type="hex"/>
|
||||
<field name="DST" low="16" high="20" type="#dst"/>
|
||||
<pattern low="27" high="31">10110</pattern>
|
||||
|
||||
<encode>
|
||||
<map name="OFFSET">src->src1</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#cread" extends="#control">
|
||||
<doc>Read from a control register.</doc>
|
||||
<display>
|
||||
{REP}cread {DST}, [{OFFSET} + {CONTROLREG}], 0x{FLAGS}
|
||||
</display>
|
||||
|
||||
<field name="CONTROLREG" low="0" high="11" type="custom"/>
|
||||
<field name="DST" low="16" high="20" type="#dst"/>
|
||||
|
||||
<encode>
|
||||
<map name="OFFSET">src->src1</map>
|
||||
<map name="CONTROLREG">src->immed</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="cread" extends="#cread">
|
||||
<gen max="5"/>
|
||||
<pattern low="27" high="31">10110</pattern>
|
||||
</bitset>
|
||||
|
||||
<!-- a6xx shuffled around the cread opcode -->
|
||||
<bitset name="cread" extends="#cread">
|
||||
<gen min="6"/>
|
||||
<pattern low="27" high="31">10111</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#branch" extends="#instruction">
|
||||
<field name="OFFSET" low="0" high="15" type="branch"/>
|
||||
<field name="SRC" low="21" high="25" type="#src"/>
|
||||
|
||||
<encode>
|
||||
<map name="OFFSET">src->offset</map>
|
||||
<map name="SRC">src->src1</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#branch-immed" extends="#branch">
|
||||
<display>
|
||||
{NAME} {SRC}, 0x{IMMED}, #{OFFSET}
|
||||
</display>
|
||||
<field name="IMMED" low="16" high="20" type="hex"/>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#branch-bit" extends="#branch">
|
||||
<display>
|
||||
{NAME} {SRC}, b{BIT}, #{OFFSET}
|
||||
</display>
|
||||
<field name="BIT" low="16" high="20" type="uint"/>
|
||||
|
||||
<encode>
|
||||
<map name="BIT">src->bit</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="brnei" displayname="brne" extends="#branch-immed">
|
||||
<doc>Branch if not equal to an immediate.</doc>
|
||||
<pattern low="26" high="31">110000</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="breqi" displayname="breq" extends="#branch-immed">
|
||||
<doc>Branch if equal to an immediate.</doc>
|
||||
<pattern low="26" high="31">110001</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="brneb" displayname="brne" extends="#branch-bit">
|
||||
<doc>Branch if a bit is not set.</doc>
|
||||
<override>
|
||||
<!-- jump #label is encoded as brne $00, b0, #label -->
|
||||
<expr>
|
||||
({BIT} == 0) && ({SRC} == 0)
|
||||
</expr>
|
||||
<display>
|
||||
jump #{OFFSET}
|
||||
</display>
|
||||
</override>
|
||||
<pattern low="26" high="31">110010</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="breqb" displayname="breq" extends="#branch-bit">
|
||||
<doc>Branch if a bit is set.</doc>
|
||||
<pattern low="26" high="31">110011</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#instruction-no-args" extends="#instruction">
|
||||
<display>
|
||||
{NAME}
|
||||
</display>
|
||||
<pattern low="0" high="25">xxxxxxxxxxxxxxxxxxxxxxxxxx</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="#ret" extends="#instruction">
|
||||
<display>
|
||||
{NAME}
|
||||
</display>
|
||||
<pattern low="26" high="31">110100</pattern>
|
||||
<pattern low="0" high="24">xxxxxxxxxxxxxxxxxxxxxxxxx</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="ret" extends="#ret">
|
||||
<pattern low="25" high="25">0</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="iret" extends="#ret">
|
||||
<doc>Return from preemption interrupt handler.</doc>
|
||||
<pattern low="25" high="25">1</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="call" extends="#instruction">
|
||||
<display>
|
||||
call #{TARGET}
|
||||
</display>
|
||||
|
||||
<field name="TARGET" low="0" high="25" type="absbranch" call="true"/>
|
||||
<pattern low="26" high="31">110101</pattern>
|
||||
|
||||
<encode>
|
||||
<map name="TARGET">src->literal</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<bitset name="waitin" extends="#instruction-no-args">
|
||||
<doc>
|
||||
A special branch instruction that parses the next PM4 packet
|
||||
header in $data and jumps to the packet handler routine. By
|
||||
convention the delay slot always contains a "mov $01, $data"
|
||||
instruction, so that $01 contains the packet header when
|
||||
processing the next packet.
|
||||
</doc>
|
||||
<display>
|
||||
waitin
|
||||
</display>
|
||||
<pattern low="26" high="31">110110</pattern>
|
||||
</bitset>
|
||||
|
||||
<bitset name="preemptleave" extends="#instruction">
|
||||
<doc>
|
||||
Try to leave the preempt handler without jumping back to the
|
||||
instruction that was interrupted. Jumps to the given destination
|
||||
if this fails.
|
||||
</doc>
|
||||
<display>
|
||||
preemptleave #{TARGET}
|
||||
</display>
|
||||
|
||||
<field name="TARGET" low="0" high="25" type="absbranch"/>
|
||||
<pattern low="26" high="31">111000</pattern>
|
||||
|
||||
<encode>
|
||||
<map name="TARGET">src->literal</map>
|
||||
</encode>
|
||||
</bitset>
|
||||
|
||||
<expr name="#three">
|
||||
3
|
||||
</expr>
|
||||
|
||||
<bitset name="setsecure" extends="#instruction-no-args">
|
||||
<doc>
|
||||
Call the zap shader fw to switch into/out of secure mode. Skips
|
||||
the next two instructions if successful.
|
||||
</doc>
|
||||
<display>
|
||||
setsecure $02, #{TARGET}
|
||||
</display>
|
||||
|
||||
<pattern low="26" high="31">111011</pattern>
|
||||
<derived name="TARGET" expr="#three" width="16" type="branch"/>
|
||||
</bitset>
|
||||
|
||||
<bitset name="nop" extends="#instruction-rep">
|
||||
<gen max="5"/>
|
||||
<display>
|
||||
{REP}nop
|
||||
</display>
|
||||
<pattern low="0" high="25">00000000000000000000000000</pattern>
|
||||
<pattern low="27" high="31">00000</pattern>
|
||||
</bitset>
|
||||
|
||||
<!-- a6xx changed the default nop pattern and all 0's is now illegal -->
|
||||
<bitset name="nop" extends="#instruction-rep">
|
||||
<gen min="6"/>
|
||||
<display>
|
||||
{REP}nop
|
||||
</display>
|
||||
<pattern low="0" high="25">01000000000000000000000000</pattern>
|
||||
<pattern low="27" high="31">00000</pattern>
|
||||
</bitset>
|
||||
|
||||
</isa>
|
||||
|
|
@ -34,26 +34,68 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "util/macros.h"
|
||||
#include "util/log.h"
|
||||
#include "afuc.h"
|
||||
#include "asm.h"
|
||||
#include "parser.h"
|
||||
#include "util.h"
|
||||
|
||||
struct encode_state {
|
||||
unsigned gen;
|
||||
};
|
||||
|
||||
static afuc_opc
|
||||
__instruction_case(struct encode_state *s, struct afuc_instr *instr)
|
||||
{
|
||||
switch (instr->opc) {
|
||||
#define ALU(name) \
|
||||
case OPC_##name: \
|
||||
if (instr->has_immed) \
|
||||
return OPC_##name##I; \
|
||||
break;
|
||||
|
||||
ALU(ADD)
|
||||
ALU(ADDHI)
|
||||
ALU(SUB)
|
||||
ALU(SUBHI)
|
||||
ALU(AND)
|
||||
ALU(OR)
|
||||
ALU(XOR)
|
||||
ALU(NOT)
|
||||
ALU(SHL)
|
||||
ALU(USHR)
|
||||
ALU(ISHR)
|
||||
ALU(ROT)
|
||||
ALU(MUL8)
|
||||
ALU(MIN)
|
||||
ALU(MAX)
|
||||
ALU(CMP)
|
||||
#undef ALU
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return instr->opc;
|
||||
}
|
||||
|
||||
#include "encode.h"
|
||||
|
||||
int gpuver;
|
||||
|
||||
/* bit lame to hard-code max but fw sizes are small */
|
||||
static struct asm_instruction instructions[0x2000];
|
||||
static struct afuc_instr instructions[0x2000];
|
||||
static unsigned num_instructions;
|
||||
|
||||
static struct asm_label labels[0x512];
|
||||
static unsigned num_labels;
|
||||
|
||||
struct asm_instruction *
|
||||
next_instr(int tok)
|
||||
struct afuc_instr *
|
||||
next_instr(afuc_opc opc)
|
||||
{
|
||||
struct asm_instruction *ai = &instructions[num_instructions++];
|
||||
struct afuc_instr *ai = &instructions[num_instructions++];
|
||||
assert(num_instructions < ARRAY_SIZE(instructions));
|
||||
ai->tok = tok;
|
||||
ai->opc = opc;
|
||||
return ai;
|
||||
}
|
||||
|
||||
|
|
@ -85,245 +127,79 @@ resolve_label(const char *str)
|
|||
exit(2);
|
||||
}
|
||||
|
||||
static afuc_opc
|
||||
tok2alu(int tok)
|
||||
{
|
||||
switch (tok) {
|
||||
case T_OP_ADD:
|
||||
return OPC_ADD;
|
||||
case T_OP_ADDHI:
|
||||
return OPC_ADDHI;
|
||||
case T_OP_SUB:
|
||||
return OPC_SUB;
|
||||
case T_OP_SUBHI:
|
||||
return OPC_SUBHI;
|
||||
case T_OP_AND:
|
||||
return OPC_AND;
|
||||
case T_OP_OR:
|
||||
return OPC_OR;
|
||||
case T_OP_XOR:
|
||||
return OPC_XOR;
|
||||
case T_OP_NOT:
|
||||
return OPC_NOT;
|
||||
case T_OP_SHL:
|
||||
return OPC_SHL;
|
||||
case T_OP_USHR:
|
||||
return OPC_USHR;
|
||||
case T_OP_ISHR:
|
||||
return OPC_ISHR;
|
||||
case T_OP_ROT:
|
||||
return OPC_ROT;
|
||||
case T_OP_MUL8:
|
||||
return OPC_MUL8;
|
||||
case T_OP_MIN:
|
||||
return OPC_MIN;
|
||||
case T_OP_MAX:
|
||||
return OPC_MAX;
|
||||
case T_OP_CMP:
|
||||
return OPC_CMP;
|
||||
case T_OP_MSB:
|
||||
return OPC_MSB;
|
||||
default:
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
emit_instructions(int outfd)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct encode_state s = {
|
||||
.gen = gpuver,
|
||||
};
|
||||
|
||||
/* there is an extra 0x00000000 which kernel strips off.. we could
|
||||
* perhaps use it for versioning.
|
||||
*/
|
||||
i = 0;
|
||||
write(outfd, &i, 4);
|
||||
|
||||
/* Expand some meta opcodes, and resolve branch targets */
|
||||
for (i = 0; i < num_instructions; i++) {
|
||||
struct asm_instruction *ai = &instructions[i];
|
||||
afuc_instr instr = {0};
|
||||
afuc_opc opc;
|
||||
struct afuc_instr *ai = &instructions[i];
|
||||
|
||||
switch (ai->opc) {
|
||||
case OPC_BREQ:
|
||||
ai->offset = resolve_label(ai->label) - i;
|
||||
if (ai->has_bit)
|
||||
ai->opc = OPC_BREQB;
|
||||
else
|
||||
ai->opc = OPC_BREQI;
|
||||
break;
|
||||
|
||||
case OPC_BRNE:
|
||||
ai->offset = resolve_label(ai->label) - i;
|
||||
if (ai->has_bit)
|
||||
ai->opc = OPC_BRNEB;
|
||||
else
|
||||
ai->opc = OPC_BRNEI;
|
||||
break;
|
||||
|
||||
case OPC_JUMP:
|
||||
ai->offset = resolve_label(ai->label) - i;
|
||||
ai->opc = OPC_BRNEB;
|
||||
ai->src1 = 0;
|
||||
ai->bit = 0;
|
||||
break;
|
||||
|
||||
case OPC_CALL:
|
||||
case OPC_PREEMPTLEAVE:
|
||||
ai->literal = resolve_label(ai->label);
|
||||
break;
|
||||
|
||||
case OPC_MOVI:
|
||||
if (ai->label)
|
||||
ai->immed = resolve_label(ai->label);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* special case, 2nd dword is patched up w/ # of instructions
|
||||
* (ie. offset of jmptbl)
|
||||
*/
|
||||
if (i == 1) {
|
||||
assert(ai->is_literal);
|
||||
assert(ai->opc == OPC_RAW_LITERAL);
|
||||
ai->literal &= ~0xffff;
|
||||
ai->literal |= num_instructions;
|
||||
}
|
||||
|
||||
if (ai->is_literal) {
|
||||
if (ai->opc == OPC_RAW_LITERAL) {
|
||||
write(outfd, &ai->literal, 4);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ai->tok) {
|
||||
case T_OP_NOP:
|
||||
opc = OPC_NOP;
|
||||
if (gpuver >= 6)
|
||||
instr.pad = 0x1000000;
|
||||
break;
|
||||
case T_OP_ADD:
|
||||
case T_OP_ADDHI:
|
||||
case T_OP_SUB:
|
||||
case T_OP_SUBHI:
|
||||
case T_OP_AND:
|
||||
case T_OP_OR:
|
||||
case T_OP_XOR:
|
||||
case T_OP_NOT:
|
||||
case T_OP_SHL:
|
||||
case T_OP_USHR:
|
||||
case T_OP_ISHR:
|
||||
case T_OP_ROT:
|
||||
case T_OP_MUL8:
|
||||
case T_OP_MIN:
|
||||
case T_OP_MAX:
|
||||
case T_OP_CMP:
|
||||
case T_OP_MSB:
|
||||
if (ai->has_immed) {
|
||||
/* MSB overlaps with STORE */
|
||||
assert(ai->tok != T_OP_MSB);
|
||||
if (ai->xmov) {
|
||||
fprintf(stderr,
|
||||
"ALU instruction cannot have immediate and xmov\n");
|
||||
exit(1);
|
||||
}
|
||||
opc = tok2alu(ai->tok);
|
||||
instr.alui.dst = ai->dst;
|
||||
instr.alui.src = ai->src1;
|
||||
instr.alui.uimm = ai->immed;
|
||||
} else {
|
||||
opc = OPC_ALU;
|
||||
instr.alu.dst = ai->dst;
|
||||
instr.alu.src1 = ai->src1;
|
||||
instr.alu.src2 = ai->src2;
|
||||
instr.alu.xmov = ai->xmov;
|
||||
instr.alu.alu = tok2alu(ai->tok);
|
||||
}
|
||||
break;
|
||||
case T_OP_MOV:
|
||||
/* move can either be encoded as movi (ie. move w/ immed) or
|
||||
* an alu instruction
|
||||
*/
|
||||
if ((ai->has_immed || ai->label) && ai->xmov) {
|
||||
fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ai->has_immed) {
|
||||
opc = OPC_MOVI;
|
||||
instr.movi.dst = ai->dst;
|
||||
instr.movi.uimm = ai->immed;
|
||||
instr.movi.shift = ai->shift;
|
||||
} else if (ai->label) {
|
||||
/* mov w/ a label is just an alias for an immediate, this
|
||||
* is useful to load the address of a constant table into
|
||||
* a register:
|
||||
*/
|
||||
opc = OPC_MOVI;
|
||||
instr.movi.dst = ai->dst;
|
||||
instr.movi.uimm = resolve_label(ai->label);
|
||||
instr.movi.shift = ai->shift;
|
||||
} else {
|
||||
/* encode as: or $dst, $00, $src */
|
||||
opc = OPC_ALU;
|
||||
instr.alu.dst = ai->dst;
|
||||
instr.alu.src1 = 0x00; /* $00 reads-back 0 */
|
||||
instr.alu.src2 = ai->src1;
|
||||
instr.alu.xmov = ai->xmov;
|
||||
instr.alu.alu = OPC_OR;
|
||||
}
|
||||
break;
|
||||
case T_OP_CWRITE:
|
||||
case T_OP_CREAD:
|
||||
case T_OP_STORE:
|
||||
case T_OP_LOAD:
|
||||
if (gpuver >= 6) {
|
||||
if (ai->tok == T_OP_CWRITE) {
|
||||
opc = OPC_CWRITE6;
|
||||
} else if (ai->tok == T_OP_CREAD) {
|
||||
opc = OPC_CREAD6;
|
||||
} else if (ai->tok == T_OP_STORE) {
|
||||
opc = OPC_STORE6;
|
||||
} else if (ai->tok == T_OP_LOAD) {
|
||||
opc = OPC_LOAD6;
|
||||
} else {
|
||||
unreachable("");
|
||||
}
|
||||
} else {
|
||||
if (ai->tok == T_OP_CWRITE) {
|
||||
opc = OPC_CWRITE5;
|
||||
} else if (ai->tok == T_OP_CREAD) {
|
||||
opc = OPC_CREAD5;
|
||||
} else if (ai->tok == T_OP_STORE || ai->tok == T_OP_LOAD) {
|
||||
fprintf(stderr, "load and store do not exist on a5xx\n");
|
||||
exit(1);
|
||||
} else {
|
||||
unreachable("");
|
||||
}
|
||||
}
|
||||
instr.control.src1 = ai->src1;
|
||||
instr.control.src2 = ai->src2;
|
||||
instr.control.flags = ai->bit;
|
||||
instr.control.uimm = ai->immed;
|
||||
break;
|
||||
case T_OP_BRNE:
|
||||
case T_OP_BREQ:
|
||||
if (ai->has_immed) {
|
||||
opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI;
|
||||
instr.br.bit_or_imm = ai->immed;
|
||||
} else {
|
||||
opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB;
|
||||
instr.br.bit_or_imm = ai->bit;
|
||||
}
|
||||
instr.br.src = ai->src1;
|
||||
instr.br.ioff = resolve_label(ai->label) - i;
|
||||
break;
|
||||
case T_OP_RET:
|
||||
opc = OPC_RET;
|
||||
break;
|
||||
case T_OP_IRET:
|
||||
opc = OPC_RET;
|
||||
instr.ret.interrupt = 1;
|
||||
break;
|
||||
case T_OP_CALL:
|
||||
opc = OPC_CALL;
|
||||
instr.call.uoff = resolve_label(ai->label);
|
||||
break;
|
||||
case T_OP_PREEMPTLEAVE:
|
||||
opc = OPC_PREEMPTLEAVE6;
|
||||
instr.call.uoff = resolve_label(ai->label);
|
||||
break;
|
||||
case T_OP_SETSECURE:
|
||||
opc = OPC_SETSECURE;
|
||||
if (resolve_label(ai->label) != i + 3) {
|
||||
fprintf(stderr, "jump label %s is incorrect for setsecure\n",
|
||||
ai->label);
|
||||
exit(1);
|
||||
}
|
||||
if (ai->src1 != 0x2) {
|
||||
fprintf(stderr, "source for setsecure must be $02\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case T_OP_JUMP:
|
||||
/* encode jump as: brne $00, b0, #label */
|
||||
opc = OPC_BRNEB;
|
||||
instr.br.bit_or_imm = 0;
|
||||
instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */
|
||||
instr.br.ioff = resolve_label(ai->label) - i;
|
||||
break;
|
||||
case T_OP_WAITIN:
|
||||
opc = OPC_WIN;
|
||||
break;
|
||||
default:
|
||||
unreachable("");
|
||||
}
|
||||
|
||||
afuc_set_opc(&instr, opc, ai->rep);
|
||||
|
||||
write(outfd, &instr, 4);
|
||||
uint32_t encoded = bitmask_to_uint64_t(encode__instruction(&s, NULL, ai));
|
||||
write(outfd, &encoded, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,37 +30,12 @@
|
|||
|
||||
extern int gpuver;
|
||||
|
||||
/**
|
||||
* Intermediate representation for an instruction, before final encoding.
|
||||
* This mostly exists because we need to resolve label offset's in a 2nd
|
||||
* pass, but also so that parser.y doesn't really need to care so much
|
||||
* about the different encodings for 2src regs vs 1src+immed, or mnemonics
|
||||
*/
|
||||
struct asm_instruction {
|
||||
int tok;
|
||||
int dst;
|
||||
int src1;
|
||||
int src2;
|
||||
int immed;
|
||||
int shift;
|
||||
int bit;
|
||||
int xmov;
|
||||
uint32_t literal;
|
||||
const char *label;
|
||||
|
||||
bool has_immed : 1;
|
||||
bool has_shift : 1;
|
||||
bool has_bit : 1;
|
||||
bool is_literal : 1;
|
||||
bool rep : 1;
|
||||
};
|
||||
|
||||
struct asm_label {
|
||||
unsigned offset;
|
||||
const char *label;
|
||||
};
|
||||
|
||||
struct asm_instruction *next_instr(int tok);
|
||||
struct afuc_instr *next_instr(afuc_opc opc);
|
||||
void decl_label(const char *str);
|
||||
|
||||
static inline uint32_t
|
||||
|
|
|
|||
|
|
@ -35,13 +35,15 @@
|
|||
|
||||
#include "util/os_file.h"
|
||||
|
||||
#include "compiler/isaspec/isaspec.h"
|
||||
|
||||
#include "freedreno_pm4.h"
|
||||
|
||||
#include "afuc.h"
|
||||
#include "util.h"
|
||||
#include "emu.h"
|
||||
|
||||
static int gpuver;
|
||||
int gpuver;
|
||||
|
||||
/* non-verbose mode should output something suitable to feed back into
|
||||
* assembler.. verbose mode has additional output useful for debugging
|
||||
|
|
@ -52,213 +54,26 @@ static bool verbose = false;
|
|||
/* emulator mode: */
|
||||
static bool emulator = false;
|
||||
|
||||
static void
|
||||
print_gpu_reg(uint32_t regbase)
|
||||
{
|
||||
if (regbase < 0x100)
|
||||
return;
|
||||
|
||||
char *name = afuc_gpu_reg_name(regbase);
|
||||
if (name) {
|
||||
printf("\t; %s", name);
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
#define printerr(fmt, ...) afuc_printc(AFUC_ERR, fmt, ##__VA_ARGS__)
|
||||
#define printlbl(fmt, ...) afuc_printc(AFUC_LBL, fmt, ##__VA_ARGS__)
|
||||
|
||||
void
|
||||
print_src(unsigned reg)
|
||||
{
|
||||
if (reg == REG_REM)
|
||||
printf("$rem"); /* remainding dwords in packet */
|
||||
else if (reg == REG_MEMDATA)
|
||||
printf("$memdata");
|
||||
else if (reg == REG_REGDATA)
|
||||
printf("$regdata");
|
||||
else if (reg == REG_DATA)
|
||||
printf("$data");
|
||||
else
|
||||
printf("$%02x", reg);
|
||||
}
|
||||
|
||||
void
|
||||
print_dst(unsigned reg)
|
||||
{
|
||||
if (reg == REG_REM)
|
||||
printf("$rem"); /* remainding dwords in packet */
|
||||
else if (reg == REG_ADDR)
|
||||
printf("$addr");
|
||||
else if (reg == REG_USRADDR)
|
||||
printf("$usraddr");
|
||||
else if (reg == REG_DATA)
|
||||
printf("$data");
|
||||
else
|
||||
printf("$%02x", reg);
|
||||
}
|
||||
|
||||
static void
|
||||
print_alu_name(afuc_opc opc, uint32_t instr)
|
||||
{
|
||||
if (opc == OPC_ADD) {
|
||||
printf("add ");
|
||||
} else if (opc == OPC_ADDHI) {
|
||||
printf("addhi ");
|
||||
} else if (opc == OPC_SUB) {
|
||||
printf("sub ");
|
||||
} else if (opc == OPC_SUBHI) {
|
||||
printf("subhi ");
|
||||
} else if (opc == OPC_AND) {
|
||||
printf("and ");
|
||||
} else if (opc == OPC_OR) {
|
||||
printf("or ");
|
||||
} else if (opc == OPC_XOR) {
|
||||
printf("xor ");
|
||||
} else if (opc == OPC_NOT) {
|
||||
printf("not ");
|
||||
} else if (opc == OPC_SHL) {
|
||||
printf("shl ");
|
||||
} else if (opc == OPC_USHR) {
|
||||
printf("ushr ");
|
||||
} else if (opc == OPC_ISHR) {
|
||||
printf("ishr ");
|
||||
} else if (opc == OPC_ROT) {
|
||||
printf("rot ");
|
||||
} else if (opc == OPC_MUL8) {
|
||||
printf("mul8 ");
|
||||
} else if (opc == OPC_MIN) {
|
||||
printf("min ");
|
||||
} else if (opc == OPC_MAX) {
|
||||
printf("max ");
|
||||
} else if (opc == OPC_CMP) {
|
||||
printf("cmp ");
|
||||
} else if (opc == OPC_MSB) {
|
||||
printf("msb ");
|
||||
} else {
|
||||
printerr("[%08x]", instr);
|
||||
printf(" ; alu%02x ", opc);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
getpm4(uint32_t id)
|
||||
{
|
||||
return afuc_pm_id_name(id);
|
||||
}
|
||||
|
||||
static struct {
|
||||
uint32_t offset;
|
||||
uint32_t num_jump_labels;
|
||||
uint32_t jump_labels[256];
|
||||
} jump_labels[1024];
|
||||
int num_jump_labels;
|
||||
|
||||
static void
|
||||
add_jump_table_entry(uint32_t n, uint32_t offset)
|
||||
print_gpu_reg(FILE *out, uint32_t regbase)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (n > 128) /* can't possibly be a PM4 PKT3.. */
|
||||
if (regbase < 0x100)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_jump_labels; i++)
|
||||
if (jump_labels[i].offset == offset)
|
||||
goto add_label;
|
||||
|
||||
num_jump_labels = i + 1;
|
||||
jump_labels[i].offset = offset;
|
||||
jump_labels[i].num_jump_labels = 0;
|
||||
|
||||
add_label:
|
||||
jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n;
|
||||
assert(jump_labels[i].num_jump_labels < 256);
|
||||
}
|
||||
|
||||
static int
|
||||
get_jump_table_entry(uint32_t offset)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_jump_labels; i++)
|
||||
if (jump_labels[i].offset == offset)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint32_t label_offsets[0x512];
|
||||
static int num_label_offsets;
|
||||
|
||||
static int
|
||||
label_idx(uint32_t offset, bool create)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_label_offsets; i++)
|
||||
if (offset == label_offsets[i])
|
||||
return i;
|
||||
if (!create)
|
||||
return -1;
|
||||
label_offsets[i] = offset;
|
||||
num_label_offsets = i + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static const char *
|
||||
label_name(uint32_t offset, bool allow_jt)
|
||||
{
|
||||
static char name[12];
|
||||
int lidx;
|
||||
|
||||
if (allow_jt) {
|
||||
lidx = get_jump_table_entry(offset);
|
||||
if (lidx >= 0) {
|
||||
int j;
|
||||
for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) {
|
||||
uint32_t jump_label = jump_labels[lidx].jump_labels[j];
|
||||
const char *str = getpm4(jump_label);
|
||||
if (str)
|
||||
return str;
|
||||
}
|
||||
// if we don't find anything w/ known name, maybe we should
|
||||
// return UNKN%d to at least make it clear that this is some
|
||||
// sort of jump-table entry?
|
||||
}
|
||||
char *name = afuc_gpu_reg_name(regbase);
|
||||
if (name) {
|
||||
fprintf(out, "\t; %s", name);
|
||||
free(name);
|
||||
}
|
||||
|
||||
lidx = label_idx(offset, false);
|
||||
if (lidx < 0)
|
||||
return NULL;
|
||||
sprintf(name, "l%03d", lidx);
|
||||
return name;
|
||||
}
|
||||
|
||||
static uint32_t fxn_offsets[0x512];
|
||||
static int num_fxn_offsets;
|
||||
|
||||
static int
|
||||
fxn_idx(uint32_t offset, bool create)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_fxn_offsets; i++)
|
||||
if (offset == fxn_offsets[i])
|
||||
return i;
|
||||
if (!create)
|
||||
return -1;
|
||||
fxn_offsets[i] = offset;
|
||||
num_fxn_offsets = i + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static const char *
|
||||
fxn_name(uint32_t offset)
|
||||
{
|
||||
static char name[14];
|
||||
int fidx = fxn_idx(offset, false);
|
||||
if (fidx < 0)
|
||||
return NULL;
|
||||
sprintf(name, "fxn%02d", fidx);
|
||||
return name;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -285,476 +100,125 @@ print_pipe_reg(uint32_t id)
|
|||
}
|
||||
}
|
||||
|
||||
struct decode_state {
|
||||
uint32_t immed;
|
||||
uint8_t shift;
|
||||
bool has_immed;
|
||||
bool dst_is_addr;
|
||||
};
|
||||
|
||||
static void
|
||||
disasm_instr(uint32_t *instrs, unsigned pc)
|
||||
field_print_cb(struct isa_print_state *state, const char *field_name, uint64_t val)
|
||||
{
|
||||
int jump_label_idx;
|
||||
afuc_instr *instr = (void *)&instrs[pc];
|
||||
const char *fname, *lname;
|
||||
afuc_opc opc;
|
||||
bool rep;
|
||||
|
||||
afuc_get_opc(instr, &opc, &rep);
|
||||
|
||||
lname = label_name(pc, false);
|
||||
fname = fxn_name(pc);
|
||||
jump_label_idx = get_jump_table_entry(pc);
|
||||
|
||||
if (jump_label_idx >= 0) {
|
||||
int j;
|
||||
printf("\n");
|
||||
for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) {
|
||||
uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j];
|
||||
const char *name = getpm4(jump_label);
|
||||
if (name) {
|
||||
printlbl("%s", name);
|
||||
} else {
|
||||
printlbl("UNKN%d", jump_label);
|
||||
}
|
||||
printf(":\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (fname) {
|
||||
printlbl("%s", fname);
|
||||
printf(":\n");
|
||||
}
|
||||
|
||||
if (lname) {
|
||||
printlbl(" %s", lname);
|
||||
printf(":");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("\t%04x: %08x ", pc, instrs[pc]);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
switch (opc) {
|
||||
case OPC_NOP: {
|
||||
/* a6xx changed the default immediate, and apparently 0
|
||||
* is illegal now.
|
||||
*/
|
||||
const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0;
|
||||
if (instrs[pc] != nop) {
|
||||
printerr("[%08x]", instrs[pc]);
|
||||
printf(" ; ");
|
||||
}
|
||||
if (rep)
|
||||
printf("(rep)");
|
||||
printf("nop");
|
||||
print_gpu_reg(instrs[pc]);
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_ADD:
|
||||
case OPC_ADDHI:
|
||||
case OPC_SUB:
|
||||
case OPC_SUBHI:
|
||||
case OPC_AND:
|
||||
case OPC_OR:
|
||||
case OPC_XOR:
|
||||
case OPC_NOT:
|
||||
case OPC_SHL:
|
||||
case OPC_USHR:
|
||||
case OPC_ISHR:
|
||||
case OPC_ROT:
|
||||
case OPC_MUL8:
|
||||
case OPC_MIN:
|
||||
case OPC_MAX:
|
||||
case OPC_CMP: {
|
||||
bool src1 = true;
|
||||
|
||||
if (opc == OPC_NOT)
|
||||
src1 = false;
|
||||
|
||||
if (rep)
|
||||
printf("(rep)");
|
||||
|
||||
print_alu_name(opc, instrs[pc]);
|
||||
print_dst(instr->alui.dst);
|
||||
printf(", ");
|
||||
if (src1) {
|
||||
print_src(instr->alui.src);
|
||||
printf(", ");
|
||||
}
|
||||
printf("0x%04x", instr->alui.uimm);
|
||||
print_gpu_reg(instr->alui.uimm);
|
||||
|
||||
/* print out unexpected bits: */
|
||||
if (verbose) {
|
||||
if (instr->alui.src && !src1)
|
||||
printerr(" (src=%02x)", instr->alui.src);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_MOVI: {
|
||||
if (rep)
|
||||
printf("(rep)");
|
||||
printf("mov ");
|
||||
print_dst(instr->movi.dst);
|
||||
printf(", 0x%04x", instr->movi.uimm);
|
||||
if (instr->movi.shift)
|
||||
printf(" << %u", instr->movi.shift);
|
||||
|
||||
if ((instr->movi.dst == REG_ADDR) && (instr->movi.shift >= 16)) {
|
||||
uint32_t val = (uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift;
|
||||
val &= ~0x40000; /* b18 seems to be a flag */
|
||||
|
||||
if ((val & 0x00ffffff) == 0) {
|
||||
printf("\t; ");
|
||||
print_pipe_reg(val >> 24);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* using mov w/ << 16 is popular way to construct a pkt7
|
||||
* header to send (for ex, from PFP to ME), so check that
|
||||
* case first
|
||||
*/
|
||||
if ((instr->movi.shift == 16) &&
|
||||
((instr->movi.uimm & 0xff00) == 0x7000)) {
|
||||
unsigned opc, p;
|
||||
|
||||
opc = instr->movi.uimm & 0x7f;
|
||||
p = pm4_odd_parity_bit(opc);
|
||||
|
||||
/* So, you'd think that checking the parity bit would be
|
||||
* a good way to rule out false positives, but seems like
|
||||
* ME doesn't really care.. at least it would filter out
|
||||
* things that look like actual legit packets between
|
||||
* PFP and ME..
|
||||
*/
|
||||
if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) {
|
||||
const char *name = getpm4(opc);
|
||||
printf("\t; ");
|
||||
if (name)
|
||||
printlbl("%s", name);
|
||||
else
|
||||
printlbl("UNKN%u", opc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
print_gpu_reg((uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift);
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_ALU: {
|
||||
bool src1 = true;
|
||||
|
||||
if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB)
|
||||
src1 = false;
|
||||
|
||||
if (instr->alu.pad)
|
||||
printf("[%08x] ; ", instrs[pc]);
|
||||
|
||||
if (rep)
|
||||
printf("(rep)");
|
||||
if (instr->alu.xmov)
|
||||
printf("(xmov%d)", instr->alu.xmov);
|
||||
|
||||
/* special case mnemonics:
|
||||
* reading $00 seems to always yield zero, and so:
|
||||
* or $dst, $00, $src -> mov $dst, $src
|
||||
* Maybe add one for negate too, ie.
|
||||
* sub $dst, $00, $src ???
|
||||
*/
|
||||
if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) {
|
||||
printf("mov ");
|
||||
src1 = false;
|
||||
if (!strcmp(field_name, "CONTROLREG")) {
|
||||
char *name = afuc_control_reg_name(val);
|
||||
if (name) {
|
||||
isa_print(state, "@%s", name);
|
||||
free(name);
|
||||
} else {
|
||||
print_alu_name(instr->alu.alu, instrs[pc]);
|
||||
isa_print(state, "0x%03x", (unsigned)val);
|
||||
}
|
||||
|
||||
print_dst(instr->alu.dst);
|
||||
if (src1) {
|
||||
printf(", ");
|
||||
print_src(instr->alu.src1);
|
||||
}
|
||||
printf(", ");
|
||||
print_src(instr->alu.src2);
|
||||
|
||||
/* print out unexpected bits: */
|
||||
if (verbose) {
|
||||
if (instr->alu.pad)
|
||||
printerr(" (pad=%01x)", instr->alu.pad);
|
||||
if (instr->alu.src1 && !src1)
|
||||
printerr(" (src1=%02x)", instr->alu.src1);
|
||||
}
|
||||
|
||||
/* xmov is a modifier that makes the processor execute up to 3
|
||||
* extra mov's after the current instruction. Given an ALU
|
||||
* instruction:
|
||||
*
|
||||
* (xmovN) alu $dst, $src1, $src2
|
||||
*
|
||||
* In all of the uses in the firmware blob, $dst and $src2 are one
|
||||
* of the "special" registers $data, $addr, $addr2. I've observed
|
||||
* that if $dst isn't "special" then it's replaced with $00
|
||||
* instead of $data, but I haven't checked what happens if $src2
|
||||
* isn't "special". Anyway, in the usual case, the HW produces a
|
||||
* count M = min(N, $rem) and then does the following:
|
||||
*
|
||||
* M = 1:
|
||||
* mov $data, $src2
|
||||
*
|
||||
* M = 2:
|
||||
* mov $data, $src2
|
||||
* mov $data, $src2
|
||||
*
|
||||
* M = 3:
|
||||
* mov $data, $src2
|
||||
* mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH)
|
||||
* mov $data, $src2
|
||||
*
|
||||
* It seems to be frequently used in combination with (rep) to
|
||||
* provide a kind of hardware-based loop unrolling, and there's
|
||||
* even a special case in the ISA to be able to do this with
|
||||
* CP_CONTEXT_REG_BUNCH. However (rep) isn't required.
|
||||
*
|
||||
* This dumps the expected extra instructions, assuming that $rem
|
||||
* isn't too small.
|
||||
*/
|
||||
if (verbose && instr->alu.xmov) {
|
||||
for (int i = 0; i < instr->alu.xmov; i++) {
|
||||
printf("\n ; mov ");
|
||||
if (instr->alu.dst < 0x1d)
|
||||
printf("$00");
|
||||
else if (instr->alu.xmov == 3 && i == 1)
|
||||
print_dst(instr->alu.dst);
|
||||
else
|
||||
printf("$data");
|
||||
printf(", ");
|
||||
print_src(instr->alu.src2);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_CWRITE6:
|
||||
case OPC_CREAD6:
|
||||
case OPC_STORE6:
|
||||
case OPC_LOAD6: {
|
||||
if (rep)
|
||||
printf("(rep)");
|
||||
|
||||
bool is_control_reg = true;
|
||||
bool is_store = true;
|
||||
if (gpuver >= 6) {
|
||||
switch (opc) {
|
||||
case OPC_CWRITE6:
|
||||
printf("cwrite ");
|
||||
break;
|
||||
case OPC_CREAD6:
|
||||
is_store = false;
|
||||
printf("cread ");
|
||||
break;
|
||||
case OPC_STORE6:
|
||||
is_control_reg = false;
|
||||
printf("store ");
|
||||
break;
|
||||
case OPC_LOAD6:
|
||||
is_control_reg = false;
|
||||
is_store = false;
|
||||
printf("load ");
|
||||
break;
|
||||
default:
|
||||
assert(!"unreachable");
|
||||
}
|
||||
} else {
|
||||
switch (opc) {
|
||||
case OPC_CWRITE5:
|
||||
printf("cwrite ");
|
||||
break;
|
||||
case OPC_CREAD5:
|
||||
is_store = false;
|
||||
printf("cread ");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "A6xx control opcode on A5xx?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_store)
|
||||
print_src(instr->control.src1);
|
||||
else
|
||||
print_dst(instr->control.src1);
|
||||
printf(", [");
|
||||
print_src(instr->control.src2);
|
||||
printf(" + ");
|
||||
if (is_control_reg && instr->control.flags != 0x4)
|
||||
print_control_reg(instr->control.uimm);
|
||||
else
|
||||
printf("0x%03x", instr->control.uimm);
|
||||
printf("], 0x%x", instr->control.flags);
|
||||
break;
|
||||
}
|
||||
case OPC_BRNEI:
|
||||
case OPC_BREQI:
|
||||
case OPC_BRNEB:
|
||||
case OPC_BREQB: {
|
||||
unsigned off = pc + instr->br.ioff;
|
||||
|
||||
assert(!rep);
|
||||
|
||||
/* Since $00 reads back zero, it can be used as src for
|
||||
* unconditional branches. (This only really makes sense
|
||||
* for the BREQB.. or possible BRNEI if imm==0.)
|
||||
*
|
||||
* If bit=0 then branch is taken if *all* bits are zero.
|
||||
* Otherwise it is taken if bit (bit-1) is clear.
|
||||
*
|
||||
* Note the instruction after a jump/branch is executed
|
||||
* regardless of whether branch is taken, so use nop or
|
||||
* take that into account in code.
|
||||
*/
|
||||
if (instr->br.src || (opc != OPC_BRNEB)) {
|
||||
bool immed = false;
|
||||
|
||||
if (opc == OPC_BRNEI) {
|
||||
printf("brne ");
|
||||
immed = true;
|
||||
} else if (opc == OPC_BREQI) {
|
||||
printf("breq ");
|
||||
immed = true;
|
||||
} else if (opc == OPC_BRNEB) {
|
||||
printf("brne ");
|
||||
} else if (opc == OPC_BREQB) {
|
||||
printf("breq ");
|
||||
}
|
||||
print_src(instr->br.src);
|
||||
if (immed) {
|
||||
printf(", 0x%x,", instr->br.bit_or_imm);
|
||||
} else {
|
||||
printf(", b%u,", instr->br.bit_or_imm);
|
||||
}
|
||||
} else {
|
||||
printf("jump");
|
||||
if (verbose && instr->br.bit_or_imm) {
|
||||
printerr(" (src=%03x, bit=%03x) ", instr->br.src,
|
||||
instr->br.bit_or_imm);
|
||||
}
|
||||
}
|
||||
|
||||
printf(" #");
|
||||
printlbl("%s", label_name(off, true));
|
||||
if (verbose)
|
||||
printf(" (#%d, %04x)", instr->br.ioff, off);
|
||||
break;
|
||||
}
|
||||
case OPC_CALL:
|
||||
assert(!rep);
|
||||
printf("call #");
|
||||
printlbl("%s", fxn_name(instr->call.uoff));
|
||||
if (verbose) {
|
||||
printf(" (%04x)", instr->call.uoff);
|
||||
if (instr->br.bit_or_imm || instr->br.src) {
|
||||
printerr(" (src=%03x, bit=%03x) ", instr->br.src,
|
||||
instr->br.bit_or_imm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPC_RET:
|
||||
assert(!rep);
|
||||
if (instr->ret.pad)
|
||||
printf("[%08x] ; ", instrs[pc]);
|
||||
if (instr->ret.interrupt)
|
||||
printf("iret");
|
||||
else
|
||||
printf("ret");
|
||||
break;
|
||||
case OPC_WIN:
|
||||
assert(!rep);
|
||||
if (instr->waitin.pad)
|
||||
printf("[%08x] ; ", instrs[pc]);
|
||||
printf("waitin");
|
||||
if (verbose && instr->waitin.pad)
|
||||
printerr(" (pad=%x)", instr->waitin.pad);
|
||||
break;
|
||||
case OPC_PREEMPTLEAVE6:
|
||||
if (gpuver < 6) {
|
||||
printf("[%08x] ; op38", instrs[pc]);
|
||||
} else {
|
||||
printf("preemptleave #");
|
||||
printlbl("%s", label_name(instr->call.uoff, true));
|
||||
}
|
||||
break;
|
||||
case OPC_SETSECURE:
|
||||
/* Note: This seems to implicitly read the secure/not-secure state
|
||||
* to set from the low bit of $02, and implicitly jumps to pc + 3
|
||||
* (i.e. skipping the next two instructions) if it succeeds. We
|
||||
* print these implicit parameters to make reading the disassembly
|
||||
* easier.
|
||||
*/
|
||||
if (instr->pad)
|
||||
printf("[%08x] ; ", instrs[pc]);
|
||||
printf("setsecure $02, #");
|
||||
printlbl("%s", label_name(pc + 3, true));
|
||||
break;
|
||||
default:
|
||||
printerr("[%08x]", instrs[pc]);
|
||||
printf(" ; op%02x ", opc);
|
||||
print_dst(instr->alui.dst);
|
||||
printf(", ");
|
||||
print_src(instr->alui.src);
|
||||
print_gpu_reg(instrs[pc] & 0xffff);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
setup_packet_table(uint32_t *jmptbl, uint32_t sizedwords)
|
||||
pre_instr_cb(void *data, unsigned n, void *instr)
|
||||
{
|
||||
num_jump_labels = 0;
|
||||
struct decode_state *state = data;
|
||||
state->has_immed = state->dst_is_addr = false;
|
||||
state->shift = 0;
|
||||
|
||||
if (verbose)
|
||||
printf("\t%04x: %08x ", n, *(uint32_t *)instr);
|
||||
}
|
||||
|
||||
static void
|
||||
field_cb(void *data, const char *field_name, struct isa_decode_value *val)
|
||||
{
|
||||
struct decode_state *state = data;
|
||||
|
||||
if (!strcmp(field_name, "RIMMED")) {
|
||||
state->immed = val->num;
|
||||
state->has_immed = true;
|
||||
}
|
||||
|
||||
if (!strcmp(field_name, "SHIFT")) {
|
||||
state->shift = val->num;
|
||||
}
|
||||
|
||||
if (!strcmp(field_name, "DST")) {
|
||||
if (val->num == REG_ADDR)
|
||||
state->dst_is_addr = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
post_instr_cb(void *data, unsigned n, void *instr)
|
||||
{
|
||||
struct decode_state *state = data;
|
||||
|
||||
if (state->has_immed) {
|
||||
uint32_t immed = state->immed << state->shift;
|
||||
if (state->dst_is_addr && state->shift >= 16) {
|
||||
immed &= ~0x40000; /* b18 disables auto-increment of address */
|
||||
if ((immed & 0x00ffffff) == 0) {
|
||||
printf("\t; ");
|
||||
print_pipe_reg(immed >> 24);
|
||||
}
|
||||
} else {
|
||||
print_gpu_reg(stdout, immed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Assume that instructions that don't match are raw data */
|
||||
static void
|
||||
no_match(FILE *out, const BITSET_WORD *bitset, size_t size)
|
||||
{
|
||||
fprintf(out, "[%08x]", bitset[0]);
|
||||
print_gpu_reg(out, bitset[0]);
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
static void
|
||||
get_decode_options(struct isa_decode_options *options)
|
||||
{
|
||||
*options = (struct isa_decode_options) {
|
||||
.gpu_id = gpuver,
|
||||
.branch_labels = true,
|
||||
.field_cb = field_cb,
|
||||
.field_print_cb = field_print_cb,
|
||||
.pre_instr_cb = pre_instr_cb,
|
||||
.post_instr_cb = post_instr_cb,
|
||||
.no_match_cb = no_match,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
disasm_instr(struct isa_decode_options *options, uint32_t *instrs, unsigned pc)
|
||||
{
|
||||
isa_disasm(&instrs[pc], 4, stdout, options);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_packet_table(struct isa_decode_options *options,
|
||||
uint32_t *jmptbl, uint32_t sizedwords)
|
||||
{
|
||||
struct isa_entrypoint *entrypoints = malloc(sizedwords * sizeof(struct isa_entrypoint));
|
||||
|
||||
for (unsigned i = 0; i < sizedwords; i++) {
|
||||
unsigned offset = jmptbl[i];
|
||||
entrypoints[i].offset = jmptbl[i];
|
||||
unsigned n = i; // + CP_NOP;
|
||||
add_jump_table_entry(n, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup_labels(uint32_t *instrs, uint32_t sizedwords)
|
||||
{
|
||||
afuc_opc opc;
|
||||
bool rep;
|
||||
|
||||
num_label_offsets = 0;
|
||||
|
||||
for (unsigned i = 0; i < sizedwords; i++) {
|
||||
afuc_instr *instr = (void *)&instrs[i];
|
||||
|
||||
afuc_get_opc(instr, &opc, &rep);
|
||||
|
||||
switch (opc) {
|
||||
case OPC_BRNEI:
|
||||
case OPC_BREQI:
|
||||
case OPC_BRNEB:
|
||||
case OPC_BREQB:
|
||||
label_idx(i + instr->br.ioff, true);
|
||||
break;
|
||||
case OPC_PREEMPTLEAVE6:
|
||||
if (gpuver >= 6)
|
||||
label_idx(instr->call.uoff, true);
|
||||
break;
|
||||
case OPC_CALL:
|
||||
fxn_idx(instr->call.uoff, true);
|
||||
break;
|
||||
case OPC_SETSECURE:
|
||||
/* this implicitly jumps to pc + 3 if successful */
|
||||
label_idx(i + 3, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
entrypoints[i].name = afuc_pm_id_name(n);
|
||||
if (!entrypoints[i].name) {
|
||||
char *name;
|
||||
asprintf(&name, "UNKN%d", n);
|
||||
entrypoints[i].name = name;
|
||||
}
|
||||
}
|
||||
|
||||
options->entrypoints = entrypoints;
|
||||
options->entrypoint_count = sizedwords;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -768,9 +232,14 @@ disasm(struct emu *emu)
|
|||
|
||||
emu_init(emu);
|
||||
|
||||
struct isa_decode_options options;
|
||||
struct decode_state state;
|
||||
get_decode_options(&options);
|
||||
options.cbdata = &state;
|
||||
|
||||
#ifdef BOOTSTRAP_DEBUG
|
||||
while (true) {
|
||||
disasm_instr(emu->instrs, emu->gpr_regs.pc);
|
||||
disasm_instr(&options, emu->instrs, emu->gpr_regs.pc);
|
||||
emu_step(emu);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -785,8 +254,7 @@ disasm(struct emu *emu)
|
|||
sizedwords = lpac_offset;
|
||||
}
|
||||
|
||||
setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
|
||||
setup_labels(emu->instrs, emu->sizedwords);
|
||||
setup_packet_table(&options, emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
/* TODO add option to emulate LPAC SQE instead: */
|
||||
if (emulator) {
|
||||
|
|
@ -795,15 +263,13 @@ disasm(struct emu *emu)
|
|||
emu_init(emu);
|
||||
|
||||
while (true) {
|
||||
disasm_instr(emu->instrs, emu->gpr_regs.pc);
|
||||
disasm_instr(&options, emu->instrs, emu->gpr_regs.pc);
|
||||
emu_step(emu);
|
||||
}
|
||||
}
|
||||
|
||||
/* print instructions: */
|
||||
for (int i = 0; i < sizedwords; i++) {
|
||||
disasm_instr(emu->instrs, i);
|
||||
}
|
||||
isa_disasm(emu->instrs, sizedwords * 4, stdout, &options);
|
||||
|
||||
if (!lpac_offset)
|
||||
return;
|
||||
|
|
@ -821,23 +287,20 @@ disasm(struct emu *emu)
|
|||
emu_init(emu);
|
||||
emu_run_bootstrap(emu);
|
||||
|
||||
setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
|
||||
setup_labels(emu->instrs, emu->sizedwords);
|
||||
setup_packet_table(&options, emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
|
||||
|
||||
/* print instructions: */
|
||||
for (int i = 0; i < emu->sizedwords; i++) {
|
||||
disasm_instr(emu->instrs, i);
|
||||
}
|
||||
isa_disasm(emu->instrs, emu->sizedwords * 4, stdout, &options);
|
||||
}
|
||||
|
||||
static void
|
||||
disasm_raw(uint32_t *instrs, int sizedwords)
|
||||
{
|
||||
setup_labels(instrs, sizedwords);
|
||||
struct isa_decode_options options;
|
||||
struct decode_state state;
|
||||
get_decode_options(&options);
|
||||
options.cbdata = &state;
|
||||
|
||||
for (int i = 0; i < sizedwords; i++) {
|
||||
disasm_instr(instrs, i);
|
||||
}
|
||||
isa_disasm(instrs, sizedwords * 4, stdout, &options);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -848,18 +311,16 @@ disasm_legacy(uint32_t *buf, int sizedwords)
|
|||
uint32_t *jmptbl = &buf[jmptbl_start];
|
||||
int i;
|
||||
|
||||
/* parse jumptable: */
|
||||
setup_packet_table(jmptbl, 0x80);
|
||||
struct isa_decode_options options;
|
||||
struct decode_state state;
|
||||
get_decode_options(&options);
|
||||
options.cbdata = &state;
|
||||
|
||||
/* do a pre-pass to find instructions that are potential branch targets,
|
||||
* and add labels for them:
|
||||
*/
|
||||
setup_labels(instrs, jmptbl_start);
|
||||
/* parse jumptable: */
|
||||
setup_packet_table(&options, jmptbl, 0x80);
|
||||
|
||||
/* print instructions: */
|
||||
for (i = 0; i < jmptbl_start; i++) {
|
||||
disasm_instr(instrs, i);
|
||||
}
|
||||
isa_disasm(instrs, sizedwords * 4, stdout, &options);
|
||||
|
||||
/* print jumptable: */
|
||||
if (verbose) {
|
||||
|
|
|
|||
|
|
@ -142,6 +142,21 @@ read_one_value(const char **val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
print_dst(unsigned reg)
|
||||
{
|
||||
if (reg == REG_REM)
|
||||
printf("$rem"); /* remainding dwords in packet */
|
||||
else if (reg == REG_ADDR)
|
||||
printf("$addr");
|
||||
else if (reg == REG_USRADDR)
|
||||
printf("$usraddr");
|
||||
else if (reg == REG_DATA)
|
||||
printf("$data");
|
||||
else
|
||||
printf("$%02x", reg);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_gpr_register(struct emu *emu, unsigned n)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,9 +34,13 @@
|
|||
|
||||
#include "freedreno_pm4.h"
|
||||
|
||||
#include "isaspec.h"
|
||||
|
||||
#include "emu.h"
|
||||
#include "util.h"
|
||||
|
||||
extern int gpuver;
|
||||
|
||||
#define rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
|
||||
#define rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))
|
||||
|
||||
|
|
@ -120,37 +124,23 @@ load_store_addr(struct emu *emu, unsigned gpr)
|
|||
}
|
||||
|
||||
static void
|
||||
emu_instr(struct emu *emu, afuc_instr *instr)
|
||||
emu_instr(struct emu *emu, struct afuc_instr *instr)
|
||||
{
|
||||
uint32_t rem = emu_get_gpr_reg(emu, REG_REM);
|
||||
afuc_opc opc;
|
||||
bool rep;
|
||||
|
||||
afuc_get_opc(instr, &opc, &rep);
|
||||
|
||||
switch (opc) {
|
||||
switch (instr->opc) {
|
||||
case OPC_NOP:
|
||||
break;
|
||||
case OPC_MSB:
|
||||
case OPC_ADD ... OPC_CMP: {
|
||||
uint32_t val = emu_alu(emu, opc,
|
||||
emu_get_gpr_reg(emu, instr->alui.src),
|
||||
instr->alui.uimm);
|
||||
emu_set_gpr_reg(emu, instr->alui.dst, val);
|
||||
break;
|
||||
}
|
||||
case OPC_MOVI: {
|
||||
uint32_t val = instr->movi.uimm << instr->movi.shift;
|
||||
emu_set_gpr_reg(emu, instr->movi.dst, val);
|
||||
break;
|
||||
}
|
||||
case OPC_ALU: {
|
||||
uint32_t val = emu_alu(emu, instr->alu.alu,
|
||||
emu_get_gpr_reg(emu, instr->alu.src1),
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_set_gpr_reg(emu, instr->alu.dst, val);
|
||||
uint32_t val = emu_alu(emu, instr->opc,
|
||||
emu_get_gpr_reg(emu, instr->src1),
|
||||
instr->has_immed ? instr->immed :
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
emu_set_gpr_reg(emu, instr->dst, val);
|
||||
|
||||
if (instr->alu.xmov) {
|
||||
unsigned m = MIN2(instr->alu.xmov, rem);
|
||||
if (instr->xmov) {
|
||||
unsigned m = MIN2(instr->xmov, rem);
|
||||
|
||||
assert(m <= 3);
|
||||
|
||||
|
|
@ -158,108 +148,113 @@ emu_instr(struct emu *emu, afuc_instr *instr)
|
|||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
emu_dump_state_change(emu);
|
||||
emu_set_gpr_reg(emu, REG_DATA,
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
} else if (m == 2) {
|
||||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
emu_dump_state_change(emu);
|
||||
emu_set_gpr_reg(emu, REG_DATA,
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
emu_dump_state_change(emu);
|
||||
emu_set_gpr_reg(emu, REG_DATA,
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
} else if (m == 3) {
|
||||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
emu_dump_state_change(emu);
|
||||
emu_set_gpr_reg(emu, REG_DATA,
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
emu_dump_state_change(emu);
|
||||
emu_set_gpr_reg(emu, instr->alu.dst,
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_set_gpr_reg(emu, instr->dst,
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
emu_dump_state_change(emu);
|
||||
emu_set_gpr_reg(emu, REG_DATA,
|
||||
emu_get_gpr_reg(emu, instr->alu.src2));
|
||||
emu_get_gpr_reg(emu, instr->src2));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPC_CWRITE6: {
|
||||
uint32_t src1 = emu_get_gpr_reg(emu, instr->control.src1);
|
||||
uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2);
|
||||
|
||||
if (instr->control.flags == 0x4) {
|
||||
emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm);
|
||||
} else if (instr->control.flags && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->control.flags);
|
||||
}
|
||||
|
||||
emu_set_control_reg(emu, src2 + instr->control.uimm, src1);
|
||||
case OPC_MOVI: {
|
||||
uint32_t val = instr->immed << instr->shift;
|
||||
emu_set_gpr_reg(emu, instr->dst, val);
|
||||
break;
|
||||
}
|
||||
case OPC_CREAD6: {
|
||||
uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2);
|
||||
case OPC_CWRITE: {
|
||||
uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
|
||||
uint32_t src2 = emu_get_gpr_reg(emu, instr->src2);
|
||||
|
||||
if (instr->control.flags == 0x4) {
|
||||
emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm);
|
||||
} else if (instr->control.flags && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->control.flags);
|
||||
if (instr->bit == 0x4) {
|
||||
emu_set_gpr_reg(emu, instr->src2, src2 + instr->immed);
|
||||
} else if (instr->bit && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->bit);
|
||||
}
|
||||
|
||||
emu_set_gpr_reg(emu, instr->control.src1,
|
||||
emu_get_control_reg(emu, src2 + instr->control.uimm));
|
||||
emu_set_control_reg(emu, src2 + instr->immed, src1);
|
||||
break;
|
||||
}
|
||||
case OPC_LOAD6: {
|
||||
uintptr_t addr = load_store_addr(emu, instr->control.src2) +
|
||||
instr->control.uimm;
|
||||
case OPC_CREAD: {
|
||||
uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
|
||||
|
||||
if (instr->control.flags == 0x4) {
|
||||
uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2);
|
||||
emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm);
|
||||
} else if (instr->control.flags && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->control.flags);
|
||||
if (instr->bit == 0x4) {
|
||||
emu_set_gpr_reg(emu, instr->src1, src1 + instr->immed);
|
||||
} else if (instr->bit && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->bit);
|
||||
}
|
||||
|
||||
emu_set_gpr_reg(emu, instr->dst,
|
||||
emu_get_control_reg(emu, src1 + instr->immed));
|
||||
break;
|
||||
}
|
||||
case OPC_LOAD: {
|
||||
uintptr_t addr = load_store_addr(emu, instr->src1) +
|
||||
instr->immed;
|
||||
|
||||
if (instr->bit == 0x4) {
|
||||
uint32_t src1 = emu_get_gpr_reg(emu, instr->src1);
|
||||
emu_set_gpr_reg(emu, instr->src1, src1 + instr->immed);
|
||||
} else if (instr->bit && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->bit);
|
||||
}
|
||||
|
||||
uint32_t val = emu_mem_read_dword(emu, addr);
|
||||
|
||||
emu_set_gpr_reg(emu, instr->control.src1, val);
|
||||
emu_set_gpr_reg(emu, instr->dst, val);
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_STORE6: {
|
||||
uintptr_t addr = load_store_addr(emu, instr->control.src2) +
|
||||
instr->control.uimm;
|
||||
case OPC_STORE: {
|
||||
uintptr_t addr = load_store_addr(emu, instr->src2) +
|
||||
instr->immed;
|
||||
|
||||
if (instr->control.flags == 0x4) {
|
||||
uint32_t src2 = emu_get_gpr_reg(emu, instr->control.src2);
|
||||
emu_set_gpr_reg(emu, instr->control.src2, src2 + instr->control.uimm);
|
||||
} else if (instr->control.flags && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->control.flags);
|
||||
if (instr->bit == 0x4) {
|
||||
uint32_t src2 = emu_get_gpr_reg(emu, instr->src2);
|
||||
emu_set_gpr_reg(emu, instr->src2, src2 + instr->immed);
|
||||
} else if (instr->bit && !emu->quiet) {
|
||||
printf("unhandled flags: %x\n", instr->bit);
|
||||
}
|
||||
|
||||
uint32_t val = emu_get_gpr_reg(emu, instr->control.src1);
|
||||
uint32_t val = emu_get_gpr_reg(emu, instr->src1);
|
||||
|
||||
emu_mem_write_dword(emu, addr, val);
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_BRNEI ... OPC_BREQB: {
|
||||
uint32_t off = emu->gpr_regs.pc + instr->br.ioff;
|
||||
uint32_t src = emu_get_gpr_reg(emu, instr->br.src);
|
||||
uint32_t off = emu->gpr_regs.pc + instr->offset;
|
||||
uint32_t src = emu_get_gpr_reg(emu, instr->src1);
|
||||
|
||||
if (opc == OPC_BRNEI) {
|
||||
if (src != instr->br.bit_or_imm)
|
||||
if (instr->opc == OPC_BRNEI) {
|
||||
if (src != instr->immed)
|
||||
emu->branch_target = off;
|
||||
} else if (opc == OPC_BREQI) {
|
||||
if (src == instr->br.bit_or_imm)
|
||||
} else if (instr->opc == OPC_BREQI) {
|
||||
if (src == instr->immed)
|
||||
emu->branch_target = off;
|
||||
} else if (opc == OPC_BRNEB) {
|
||||
if (!(src & (1 << instr->br.bit_or_imm)))
|
||||
} else if (instr->opc == OPC_BRNEB) {
|
||||
if (!(src & (1 << instr->bit)))
|
||||
emu->branch_target = off;
|
||||
} else if (opc == OPC_BREQB) {
|
||||
if (src & (1 << instr->br.bit_or_imm))
|
||||
} else if (instr->opc == OPC_BREQB) {
|
||||
if (src & (1 << instr->bit))
|
||||
emu->branch_target = off;
|
||||
} else {
|
||||
assert(0);
|
||||
|
|
@ -281,11 +276,11 @@ emu_instr(struct emu *emu, afuc_instr *instr)
|
|||
* presumably the return PC is two instructions later:
|
||||
*/
|
||||
emu->call_stack[emu->call_stack_idx++] = emu->gpr_regs.pc + 2;
|
||||
emu->branch_target = instr->call.uoff;
|
||||
emu->branch_target = instr->literal;
|
||||
|
||||
break;
|
||||
}
|
||||
case OPC_WIN: {
|
||||
case OPC_WAITIN: {
|
||||
assert(!emu->branch_target);
|
||||
emu->run_mode = false;
|
||||
emu->waitin = true;
|
||||
|
|
@ -298,11 +293,11 @@ emu_instr(struct emu *emu, afuc_instr *instr)
|
|||
break;
|
||||
}
|
||||
default:
|
||||
printf("unhandled opc: 0x%02x\n", opc);
|
||||
printf("unhandled opc: 0x%02x\n", instr->opc);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (rep) {
|
||||
if (instr->rep) {
|
||||
assert(rem > 0);
|
||||
emu_set_gpr_reg(emu, REG_REM, --rem);
|
||||
}
|
||||
|
|
@ -311,9 +306,26 @@ emu_instr(struct emu *emu, afuc_instr *instr)
|
|||
void
|
||||
emu_step(struct emu *emu)
|
||||
{
|
||||
afuc_instr *instr = (void *)&emu->instrs[emu->gpr_regs.pc];
|
||||
afuc_opc opc;
|
||||
bool rep;
|
||||
struct afuc_instr *instr;
|
||||
bool decoded = isa_decode((void *)&instr,
|
||||
(void *)&emu->instrs[emu->gpr_regs.pc],
|
||||
&(struct isa_decode_options) {
|
||||
.gpu_id = gpuver,
|
||||
});
|
||||
|
||||
if (!decoded) {
|
||||
uint32_t instr_val = emu->instrs[emu->gpr_regs.pc];
|
||||
if ((instr_val >> 27) == 0) {
|
||||
/* This is printed as an undecoded literal to show the immediate
|
||||
* payload, but when executing it's just a NOP.
|
||||
*/
|
||||
instr = calloc(1, sizeof(struct afuc_instr));
|
||||
instr->opc = OPC_NOP;
|
||||
} else {
|
||||
printf("unmatched instruction: 0x%08x\n", instr_val);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
emu_main_prompt(emu);
|
||||
|
||||
|
|
@ -323,9 +335,7 @@ emu_step(struct emu *emu)
|
|||
bool waitin = emu->waitin;
|
||||
emu->waitin = false;
|
||||
|
||||
afuc_get_opc(instr, &opc, &rep);
|
||||
|
||||
if (rep) {
|
||||
if (instr->rep) {
|
||||
do {
|
||||
if (!emu_get_gpr_reg(emu, REG_REM))
|
||||
break;
|
||||
|
|
@ -380,6 +390,8 @@ emu_step(struct emu *emu)
|
|||
}
|
||||
|
||||
emu_dump_state_change(emu);
|
||||
|
||||
free(instr);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
68
src/freedreno/afuc/isa.h
Normal file
68
src/freedreno/afuc/isa.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright © 2020 Google, Inc.
|
||||
* Copyright © 2023 Valve Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _ISA_H_
|
||||
#define _ISA_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "compiler/isaspec/isaspec.h"
|
||||
#include "afuc.h"
|
||||
|
||||
static inline struct afuc_instr *__instruction_create(afuc_opc opc)
|
||||
{
|
||||
struct afuc_instr *instr = calloc(1, sizeof(struct afuc_instr));
|
||||
|
||||
switch (opc) {
|
||||
#define ALU(name) \
|
||||
case OPC_##name##I: \
|
||||
instr->opc = OPC_##name; \
|
||||
instr->has_immed = true; \
|
||||
break;
|
||||
ALU(ADD)
|
||||
ALU(ADDHI)
|
||||
ALU(SUB)
|
||||
ALU(SUBHI)
|
||||
ALU(AND)
|
||||
ALU(OR)
|
||||
ALU(XOR)
|
||||
ALU(NOT)
|
||||
ALU(SHL)
|
||||
ALU(USHR)
|
||||
ALU(ISHR)
|
||||
ALU(ROT)
|
||||
ALU(MUL8)
|
||||
ALU(MIN)
|
||||
ALU(MAX)
|
||||
ALU(CMP)
|
||||
#undef ALU
|
||||
|
||||
default:
|
||||
instr->opc = opc;
|
||||
}
|
||||
|
||||
return instr;
|
||||
}
|
||||
|
||||
#endif /* _ISA_H_ */
|
||||
|
|
@ -40,6 +40,15 @@ afuc_lexer = custom_target(
|
|||
]
|
||||
)
|
||||
|
||||
encode_h = custom_target(
|
||||
'encode.h',
|
||||
input: ['afuc.xml'],
|
||||
output: 'encode.h',
|
||||
command: [
|
||||
prog_isaspec_encode, '--xml', '@INPUT@', '--out-h', '@OUTPUT@'
|
||||
],
|
||||
)
|
||||
|
||||
asm = executable(
|
||||
'afuc-asm',
|
||||
[
|
||||
|
|
@ -48,6 +57,7 @@ asm = executable(
|
|||
'util.h',
|
||||
afuc_lexer,
|
||||
afuc_parser,
|
||||
encode_h,
|
||||
],
|
||||
include_directories: [
|
||||
inc_freedreno_rnn, inc_include, inc_src, inc_util,
|
||||
|
|
@ -72,6 +82,16 @@ if with_tests
|
|||
)
|
||||
endif
|
||||
|
||||
afuc_isa = custom_target(
|
||||
'afuc-isa',
|
||||
input: ['afuc.xml'],
|
||||
output: ['afuc-isa.c', 'afuc-isa.h'],
|
||||
command: [
|
||||
prog_isaspec_decode, '--xml', '@INPUT@',
|
||||
'--out-c', '@OUTPUT0@', '--out-h', '@OUTPUT1@',
|
||||
],
|
||||
)
|
||||
|
||||
# Disasm requires mmaping >4GB
|
||||
if cc.sizeof('size_t') > 4
|
||||
disasm = executable(
|
||||
|
|
@ -85,6 +105,7 @@ if cc.sizeof('size_t') > 4
|
|||
'emu-ui.c',
|
||||
'util.c',
|
||||
'util.h',
|
||||
afuc_isa,
|
||||
],
|
||||
include_directories: [
|
||||
inc_freedreno,
|
||||
|
|
@ -96,8 +117,7 @@ if cc.sizeof('size_t') > 4
|
|||
link_with: [
|
||||
libfreedreno_rnn,
|
||||
],
|
||||
dependencies: [
|
||||
],
|
||||
dependencies: [idep_mesautil, idep_isaspec_decode],
|
||||
build_by_default : with_tools.contains('freedreno'),
|
||||
install: install_fd_decode_tools,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,12 +50,12 @@ void yyerror(const char *error)
|
|||
fprintf(stderr, "error at line %d: %s\n", yyget_lineno(), error);
|
||||
}
|
||||
|
||||
static struct asm_instruction *instr; /* current instruction */
|
||||
static struct afuc_instr *instr; /* current instruction */
|
||||
|
||||
static void
|
||||
new_instr(int tok)
|
||||
new_instr(afuc_opc opc)
|
||||
{
|
||||
instr = next_instr(tok);
|
||||
instr = next_instr(opc);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -182,9 +182,10 @@ instr_or_label: instr_r
|
|||
| T_LABEL_DECL { decl_label($1); }
|
||||
|
||||
/* instructions that can optionally have (rep) flag: */
|
||||
instr_r: alu_instr
|
||||
instr_r: alu_instr { instr->xmov = 0; }
|
||||
| T_XMOV alu_instr { instr->xmov = $1; }
|
||||
| config_instr
|
||||
| load_instr
|
||||
| store_instr
|
||||
|
||||
/* need to special case:
|
||||
* - not (single src, possibly an immediate)
|
||||
|
|
@ -193,36 +194,36 @@ instr_r: alu_instr
|
|||
* from the other ALU instructions:
|
||||
*/
|
||||
|
||||
alu_msb_instr: T_OP_MSB reg ',' reg { new_instr($1); dst($2); src2($4); }
|
||||
alu_msb_instr: T_OP_MSB reg ',' reg { new_instr(OPC_MSB); dst($2); src1($4); }
|
||||
|
||||
alu_not_instr: T_OP_NOT reg ',' reg { new_instr($1); dst($2); src2($4); }
|
||||
| T_OP_NOT reg ',' immediate { new_instr($1); dst($2); immed($4); }
|
||||
alu_not_instr: T_OP_NOT reg ',' reg { new_instr(OPC_NOT); dst($2); src1($4); }
|
||||
| T_OP_NOT reg ',' immediate { new_instr(OPC_NOT); dst($2); immed($4); }
|
||||
|
||||
alu_mov_instr: T_OP_MOV reg ',' reg { new_instr($1); dst($2); src1($4); }
|
||||
alu_mov_instr: T_OP_MOV reg ',' reg { new_instr(OPC_OR); dst($2); src1(0); src2($4); }
|
||||
| T_OP_MOV reg ',' immediate T_LSHIFT immediate {
|
||||
new_instr($1); dst($2); immed($4); shift($6);
|
||||
new_instr(OPC_MOVI); dst($2); immed($4); shift($6);
|
||||
}
|
||||
| T_OP_MOV reg ',' immediate { new_instr($1); dst($2); immed($4); }
|
||||
| T_OP_MOV reg ',' immediate { new_instr(OPC_MOVI); dst($2); immed($4); shift(0); }
|
||||
| T_OP_MOV reg ',' T_LABEL_REF T_LSHIFT immediate {
|
||||
new_instr($1); dst($2); label($4); shift($6);
|
||||
new_instr(OPC_MOVI); dst($2); label($4); shift($6);
|
||||
}
|
||||
| T_OP_MOV reg ',' T_LABEL_REF { new_instr($1); dst($2); label($4); }
|
||||
| T_OP_MOV reg ',' T_LABEL_REF { new_instr(OPC_MOVI); dst($2); label($4); shift(0); }
|
||||
|
||||
alu_2src_op: T_OP_ADD { new_instr($1); }
|
||||
| T_OP_ADDHI { new_instr($1); }
|
||||
| T_OP_SUB { new_instr($1); }
|
||||
| T_OP_SUBHI { new_instr($1); }
|
||||
| T_OP_AND { new_instr($1); }
|
||||
| T_OP_OR { new_instr($1); }
|
||||
| T_OP_XOR { new_instr($1); }
|
||||
| T_OP_SHL { new_instr($1); }
|
||||
| T_OP_USHR { new_instr($1); }
|
||||
| T_OP_ISHR { new_instr($1); }
|
||||
| T_OP_ROT { new_instr($1); }
|
||||
| T_OP_MUL8 { new_instr($1); }
|
||||
| T_OP_MIN { new_instr($1); }
|
||||
| T_OP_MAX { new_instr($1); }
|
||||
| T_OP_CMP { new_instr($1); }
|
||||
alu_2src_op: T_OP_ADD { new_instr(OPC_ADD); }
|
||||
| T_OP_ADDHI { new_instr(OPC_ADDHI); }
|
||||
| T_OP_SUB { new_instr(OPC_SUB); }
|
||||
| T_OP_SUBHI { new_instr(OPC_SUBHI); }
|
||||
| T_OP_AND { new_instr(OPC_AND); }
|
||||
| T_OP_OR { new_instr(OPC_OR); }
|
||||
| T_OP_XOR { new_instr(OPC_XOR); }
|
||||
| T_OP_SHL { new_instr(OPC_SHL); }
|
||||
| T_OP_USHR { new_instr(OPC_USHR); }
|
||||
| T_OP_ISHR { new_instr(OPC_ISHR); }
|
||||
| T_OP_ROT { new_instr(OPC_ROT); }
|
||||
| T_OP_MUL8 { new_instr(OPC_MUL8); }
|
||||
| T_OP_MIN { new_instr(OPC_MIN); }
|
||||
| T_OP_MAX { new_instr(OPC_MAX); }
|
||||
| T_OP_CMP { new_instr(OPC_CMP); }
|
||||
|
||||
alu_2src_instr: alu_2src_op reg ',' reg ',' reg { dst($2); src1($4); src2($6); }
|
||||
| alu_2src_op reg ',' reg ',' immediate { dst($2); src1($4); immed($6); }
|
||||
|
|
@ -232,30 +233,33 @@ alu_instr: alu_2src_instr
|
|||
| alu_not_instr
|
||||
| alu_mov_instr
|
||||
|
||||
config_op: T_OP_CWRITE { new_instr($1); }
|
||||
| T_OP_CREAD { new_instr($1); }
|
||||
| T_OP_LOAD { new_instr($1); }
|
||||
| T_OP_STORE { new_instr($1); }
|
||||
load_op: T_OP_LOAD { new_instr(OPC_LOAD); }
|
||||
| T_OP_CREAD { new_instr(OPC_CREAD); }
|
||||
store_op: T_OP_STORE { new_instr(OPC_STORE); }
|
||||
| T_OP_CWRITE { new_instr(OPC_CWRITE); }
|
||||
|
||||
config_instr: config_op reg ',' '[' reg '+' immediate ']' ',' immediate {
|
||||
load_instr: load_op reg ',' '[' reg '+' immediate ']' ',' immediate {
|
||||
dst($2); src1($5); immed($7); bit($10);
|
||||
}
|
||||
store_instr: store_op reg ',' '[' reg '+' immediate ']' ',' immediate {
|
||||
src1($2); src2($5); immed($7); bit($10);
|
||||
}
|
||||
|
||||
branch_op: T_OP_BRNE { new_instr($1); }
|
||||
| T_OP_BREQ { new_instr($1); }
|
||||
branch_op: T_OP_BRNE { new_instr(OPC_BRNE); }
|
||||
| T_OP_BREQ { new_instr(OPC_BREQ); }
|
||||
|
||||
branch_instr: branch_op reg ',' T_BIT ',' T_LABEL_REF { src1($2); bit($4); label($6); }
|
||||
| branch_op reg ',' immediate ',' T_LABEL_REF { src1($2); immed($4); label($6); }
|
||||
|
||||
other_instr: T_OP_CALL T_LABEL_REF { new_instr($1); label($2); }
|
||||
| T_OP_PREEMPTLEAVE T_LABEL_REF { new_instr($1); label($2); }
|
||||
| T_OP_SETSECURE reg ',' T_LABEL_REF { new_instr($1); src1($2); label($4); }
|
||||
| T_OP_RET { new_instr($1); }
|
||||
| T_OP_IRET { new_instr($1); }
|
||||
| T_OP_JUMP T_LABEL_REF { new_instr($1); label($2); }
|
||||
| T_OP_WAITIN { new_instr($1); }
|
||||
| T_OP_NOP { new_instr($1); }
|
||||
| T_LITERAL { new_instr($1); literal($1); }
|
||||
other_instr: T_OP_CALL T_LABEL_REF { new_instr(OPC_CALL); label($2); }
|
||||
| T_OP_PREEMPTLEAVE T_LABEL_REF { new_instr(OPC_PREEMPTLEAVE); label($2); }
|
||||
| T_OP_SETSECURE reg ',' T_LABEL_REF { new_instr(OPC_SETSECURE); src1($2); label($4); }
|
||||
| T_OP_RET { new_instr(OPC_RET); }
|
||||
| T_OP_IRET { new_instr(OPC_IRET); }
|
||||
| T_OP_JUMP T_LABEL_REF { new_instr(OPC_JUMP); label($2); }
|
||||
| T_OP_WAITIN { new_instr(OPC_WAITIN); }
|
||||
| T_OP_NOP { new_instr(OPC_NOP); }
|
||||
| T_LITERAL { new_instr(OPC_RAW_LITERAL); literal($1); }
|
||||
|
||||
reg: T_REGISTER
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue