summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--doc/kernel_interface.md92
-rw-r--r--native/replay/ps_sandbox_etna.c3
-rw-r--r--rnndb/isa.xml87
-rwxr-xr-xtools/asm.py324
-rwxr-xr-xtools/disasm.py182
-rw-r--r--tools/etnaviv/asm_common.py229
-rw-r--r--tools/etnaviv/parse_rng.py7
8 files changed, 739 insertions, 195 deletions
diff --git a/README.md b/README.md
index 765123a..87688f2 100644
--- a/README.md
+++ b/README.md
@@ -48,16 +48,18 @@ Vivante has a unified, fixed-size, predictable instruction format with explicit
and outputs. This does simplify code generation, compared to a weird flow
pipe system like the Mali 200/400.
-Disassembler
--------------
+Assembler and disassembler
+----------------------------
A basic disassembler for the shader instructions can be found in the tools directory:
- tools/disass.py
+ tools/disasm.py rnn/isa.xml <shader.bin>
This can be used to disassemble shaders extracted using `dump_cmdstream.py --dump-shaders`.
-An assembler still needs to be written.
+There is also an assembler:
+
+ tools/asm.py rnn/isa.xml <shader.asm> -o <shader.bin>
Command stream format
-----------------------
diff --git a/doc/kernel_interface.md b/doc/kernel_interface.md
index 6952e37..8980c71 100644
--- a/doc/kernel_interface.md
+++ b/doc/kernel_interface.md
@@ -198,3 +198,95 @@ At least in the v2 kernel driver they are not used. They are used for building t
userspace driver, but not for using it.
+Profiling
+===============
+
+HW profiling registers can be read using the ioctl:
+
+ gcvHAL_READ_ALL_PROFILE_REGISTERS
+
+This will return a structure `gcsPROFILER_COUNTERS`, defined in `GC_HAL_PROFILER.h`, which has the following timers:
+
+Hardware-wise, the memory controller keeps track of these counters in registers `MC_PROFILE_xx_READ`,
+switched by corresponding bits in registers `MC_PROFILE_CONFIGx`.
+
+HW static counters (clock rates). These are not filled in by the kernel, it appears.
+
+ gpuClock
+ axiClock
+ shaderClock
+
+HW variable counters
+
+ gpuClockStart
+ gpuClockEnd
+ gpuCyclesCounter
+ gpuTotalRead64BytesPerFrame
+ gpuTotalWrite64BytesPerFrame
+
+PE (Pixel engine)
+
+ pe_pixel_count_killed_by_color_pipe
+ pe_pixel_count_killed_by_depth_pipe
+ pe_pixel_count_drawn_by_color_pipe
+ pe_pixel_count_drawn_by_depth_pipe
+
+SH (Shader engine)
+
+ ps_inst_counter
+ rendered_pixel_counter
+ vs_inst_counter
+ rendered_vertice_counter
+ vtx_branch_inst_counter
+ vtx_texld_inst_counter
+ pxl_branch_inst_counter
+ pxl_texld_inst_counter
+
+PA (Primitive assembly)
+
+ pa_input_vtx_counter
+ pa_input_prim_counter
+ pa_output_prim_counter
+ pa_depth_clipped_counter
+ pa_trivial_rejected_counter
+ pa_culled_counter
+
+SE (Setup engine)
+
+ se_culled_triangle_count
+ se_culled_lines_count
+
+RA (Rasterizer)
+
+ ra_valid_pixel_count
+ ra_total_quad_count
+ ra_valid_quad_count_after_early_z
+ ra_total_primitive_count
+ ra_pipe_cache_miss_counter
+ ra_prefetch_cache_miss_counter
+ ra_eez_culled_counter
+
+TX (Texture engine)
+
+ tx_total_bilinear_requests
+ tx_total_trilinear_requests
+ tx_total_discarded_texture_requests
+ tx_total_texture_requests
+ tx_mem_read_count
+ tx_mem_read_in_8B_count
+ tx_cache_miss_count
+ tx_cache_hit_texel_count
+ tx_cache_miss_texel_count
+
+MC (Memory controller)
+
+ mc_total_read_req_8B_from_pipeline
+ mc_total_read_req_8B_from_IP
+ mc_total_write_req_8B_from_pipeline
+
+HI (Host interface)
+
+ hi_axi_cycles_read_request_stalled
+ hi_axi_cycles_write_request_stalled
+ hi_axi_cycles_write_data_stalled
+
diff --git a/native/replay/ps_sandbox_etna.c b/native/replay/ps_sandbox_etna.c
index aca3aaf..604427c 100644
--- a/native/replay/ps_sandbox_etna.c
+++ b/native/replay/ps_sandbox_etna.c
@@ -652,8 +652,9 @@ int main(int argc, char **argv)
/*
0x06011009, 0x00000000, 0x00000000, 0x20100008,
*/
+ /* appears that invalid instructions are simply ignored */
/* r=r+g component */
- 0x00811001, 0x00001800, 0x00000000, 0x00154018,
+ 0x00811013, 0x00001800, 0x00000000, 0x00154018,
0x07011009, 0x00000000, 0x00000000, 0x20100008,
};
diff --git a/rnndb/isa.xml b/rnndb/isa.xml
index d812420..1820024 100644
--- a/rnndb/isa.xml
+++ b/rnndb/isa.xml
@@ -27,14 +27,6 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
<domain name="VIV_ISA">
<!-- XXX still unsure if rules-ng is a suitable format for ISA descriptions,
I don't really think so, but it will initially help to put notes in a more structured format.
-
- Anyone know an description format better suited, and which
- allows automatic generation of assemblers/disassemblers/code generators if possible?
- (LLVM tblgen maybe?).
-
- The Envytools disassembler is table-based and could be adopted,
- it would have to be modified to support 128-bit instructions.
-
-->
<enum name="INST_OPCODE" brief="Main opcode table">
@@ -92,7 +84,14 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
</doc>
</value>
<value value="0x0A" name="MOVAR" brief="Move address to address register"/>
- <value value="0x0B" name="MOVAF" brief="Move float to address register"/>
+ <value value="0x0B" name="MOVAF" brief="Move float to address register">
+ <doc>
+ dst := src2
+
+ Copies the floating point value of operand src2 to address register dst.
+ XXX does this round or floor?
+ </doc>
+ </value>
<value value="0x0C" name="RCP" brief="Reciprocal">
<doc>
dst := 1.0 / src2
@@ -139,13 +138,57 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
<value value="0x1E" name="ENDREP" brief="Ends a REPEAT block"/>
<value value="0x1F" name="LOOP" brief="Begins a LOOP block"/>
<value value="0x20" name="ENDLOOP" brief="Ends a LOOP block"/>
- <value value="0x21" name="SQRT" brief="Square root"/> <!-- HAS_SQRT_TRIG -->
- <value value="0x22" name="SIN" brief="Sine"/> <!-- HAS_SQRT_TRIG -->
- <value value="0x23" name="COS" brief="Cosine"/> <!-- HAS_SQRT_TRIG -->
+ <value value="0x21" name="SQRT" brief="Square root"> <!-- HAS_SQRT_TRIG -->
+ <doc>
+ dst := sqrt(src2)
+
+ Computes the square root of src2 and puts the result in temporary register dst.
+ </doc>
+ </value>
+ <value value="0x22" name="SIN" brief="Sine"> <!-- HAS_SQRT_TRIG -->
+ <doc>
+ dst := sin(src2 * (PI/2))
+
+ Computes the sine of src2 and puts the result in temporary register dst.
+
+ The period of the sine is 4 and not 2 PI, thus to get normal behavior the instruction
+ should be prefixed by a division by PI/2.
+ </doc>
+ </value>
+ <value value="0x23" name="COS" brief="Cosine"> <!-- HAS_SQRT_TRIG -->
+ <doc>
+ dst := cos(src2 * (PI/2))
+
+ Computes the cosine of src2 and puts the result in temporary register dst.
+
+ The period of the cosine is 4 and not 2 PI, thus to get normal behavior the instruction
+ should be prefixed by a division by PI/2.
+ </doc>
+ </value>
<value value="0x24" name="POLY"/>
- <value value="0x25" name="FLOOR" brief="Largest integral value not greater than the argument"/> <!-- HAS_SIGN_FLOOR_CEIL -->
- <value value="0x26" name="CEIL" brief="Smallest integral value not less than the argument"/> <!-- HAS_SIGN_FLOOR_CEIL -->
- <value value="0x27" name="SIGN" brief="Return sign of the argument"/> <!-- HAS_SIGN_FLOOR_CEIL -->
+ <value value="0x25" name="FLOOR" brief="Largest integral value not greater than the argument"> <!-- HAS_SIGN_FLOOR_CEIL -->
+ <doc>
+ dst := floor(src2)
+
+ Computes the largest integral value not greater than the argument, and puts the result in temporary
+ register dst.
+ </doc>
+ </value>
+ <value value="0x26" name="CEIL" brief="Smallest integral value not less than the argument"> <!-- HAS_SIGN_FLOOR_CEIL -->
+ <doc>
+ dst := ceil(src2)
+
+ Computes the smallest integral value not less than the argument, and puts the result in temporary
+ register dst.
+ </doc>
+ </value>
+ <value value="0x27" name="SIGN" brief="Return sign of the argument"> <!-- HAS_SIGN_FLOOR_CEIL -->
+ <doc>
+ dst := sign(src2)
+
+ Return 1.0 if the sign is positive or zero, -1.0 if negative.
+ </doc>
+ </value>
<value value="0x28" name="ADDLO"/>
<value value="0x29" name="MULLO"/>
<value value="0x2A" name="BARRIER" brief="Thread barrier"/>
@@ -292,7 +335,7 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
<!-- operand 0 -->
<bitfield high="11" low="11" name="SRC0_USE" brief="Source operand 0 used"/>
<bitfield high="20" low="12" name="SRC0_REG" brief="Source operand 0 register"/>
- <!-- bit 21? -->
+ <bitfield high="21" low="21" name="UNK1_21"/>
<bitfield high="29" low="22" name="SRC0_SWIZ" type="INST_SWIZ" brief="Source operand 0 swizzle"/>
<bitfield high="30" low="30" name="SRC0_NEG" brief="Source operand 0 negate"/>
<bitfield high="31" low="31" name="SRC0_ABS" brief="Source operand 0 absolute"/>
@@ -303,26 +346,28 @@ xsi:schemaLocation="http://nouveau.freedesktop.org/ rules-ng.xsd">
<!-- operand 1 -->
<bitfield high="6" low="6" name="SRC1_USE" brief="Source operand 1 used"/>
<bitfield high="15" low="7" name="SRC1_REG" brief="Source operand 1 register"/>
- <!-- bit 16? -->
+ <bitfield high="16" low="16" name="UNK2_16"/>
<bitfield high="24" low="17" name="SRC1_SWIZ" type="INST_SWIZ" brief="Source operand 1 swizzle"/>
<bitfield high="25" low="25" name="SRC1_NEG" brief="Source operand 1 negate"/>
<bitfield high="26" low="26" name="SRC1_ABS" brief="Source operand 1 absolute"/>
<bitfield high="29" low="27" name="SRC1_AMODE" type="INST_AMODE" brief="Source operand 1 addressing mode"/>
- <!-- bit 30,31? -->
+ <bitfield high="31" low="30" name="UNK2_30"/>
</reg32>
<reg32 offset="0x0000C" name="WORD_3">
<bitfield high="2" low="0" name="SRC1_RGROUP" type="INST_RGROUP" brief="Source operand 1 register group"/>
+ <!-- bits 7..21: instruction address, effectively takes the place of src2 operand -->
+ <bitfield high="21" low="7" name="SRC2_IMM" brief="Immediate (address) operand"/>
<!-- operand 2 -->
<bitfield high="3" low="3" name="SRC2_USE" brief="Source operand 2 used"/>
<bitfield high="12" low="4" name="SRC2_REG" brief="Source operand 2 register"/>
- <!-- bit 13? -->
+ <bitfield high="13" low="13" name="UNK3_13"/>
<bitfield high="21" low="14" name="SRC2_SWIZ" type="INST_SWIZ" brief="Source operand 2 swizzle"/>
<bitfield high="22" low="22" name="SRC2_NEG" brief="Source operand 2 negate"/>
<bitfield high="23" low="23" name="SRC2_ABS" brief="Source operand 2 absolute"/>
- <!-- bit 24? -->
+ <bitfield high="24" low="24" name="UNK3_24"/>
<bitfield high="27" low="25" name="SRC2_AMODE" type="INST_AMODE" brief="Source operand 2 addressing mode"/>
<bitfield high="30" low="28" name="SRC2_RGROUP" type="INST_RGROUP" brief="Source operand 2 register group"/>
- <!-- bit 31? -->
+ <bitfield high="31" low="31" name="UNK3_31"/>
</reg32>
</domain>
diff --git a/tools/asm.py b/tools/asm.py
new file mode 100755
index 0000000..4794b98
--- /dev/null
+++ b/tools/asm.py
@@ -0,0 +1,324 @@
+#!/usr/bin/python
+'''
+Shader assembler.
+
+Usage: asm.py ../rnndb/isa.xml in.asm out.bin
+'''
+# Copyright (c) 2012-2013 Wladimir J. van der Laan
+#
+# 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, sub license,
+# 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 NON-INFRINGEMENT. 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.
+from __future__ import print_function, division, unicode_literals
+import argparse,struct
+import sys
+from binascii import b2a_hex
+import re
+
+from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain
+from etnaviv.asm_common import DstOperand, DstOperandAReg, SrcOperand, TexOperand, AddrOperand, Instruction, AMODES, COMPS
+from etnaviv.asm_common import disassemble, format_instruction
+
+reg_re = re.compile('^(t|u|a|tex)(\d+)(\[.*?\])?(\.[xyzw]{1,4})?$')
+label_re = re.compile('^[0-9a-zA-Z\-\_]+$')
+
+def parse_amode(amode):
+ if not amode:
+ return 0
+ return AMODES.index(amode[1:-1])
+
+def parse_comps(comps):
+ if not comps:
+ return 15
+ return ((('x' in comps)<<0)|(('y' in comps)<<1)|(('z' in comps)<<2)|(('w' in comps)<<3))
+
+def parse_swiz(swiz):
+ if not swiz:
+ return 0xe4
+ rv = 0
+ for idx in xrange(4):
+ if idx < len(swiz):
+ comp = COMPS.index(swiz[idx+1])
+ rv |= comp << (idx * 2)
+ return rv
+
+def assemble(isa, inst, warnings):
+ fields = {}
+ fields['OPCODE'] = inst.op
+ fields['COND'] = inst.cond
+ fields['SAT'] = inst.sat
+
+ if isinstance(inst.dst, DstOperandAReg):
+ # XXX validate that this instruction accepts
+ # address destination arguments
+ fields['DST_REG'] = inst.dst.reg
+ fields['DST_COMPS'] = inst.dst.comps
+ elif isinstance(inst.dst, DstOperand):
+ fields['DST_USE'] = inst.dst.use
+ fields['DST_AMODE'] = inst.dst.amode
+ fields['DST_REG'] = inst.dst.reg
+ fields['DST_COMPS'] = inst.dst.comps
+ elif inst.dst is None:
+ fields['DST_USE'] = 0
+ else:
+ warnings.append('Invalid destination argument')
+
+ if inst.tex is not None:
+ fields['TEX_ID'] = inst.tex.id
+ fields['TEX_AMODE'] = inst.tex.amode
+ fields['TEX_SWIZ'] = inst.tex.swiz
+
+ if inst.addr is not None:
+ fields['SRC2_IMM'] = inst.addr.addr
+
+ for (idx, src) in enumerate(inst.src):
+ if src is not None:
+ fields['SRC%i_USE' % idx] = src.use
+ fields['SRC%i_REG' % idx] = src.reg
+ fields['SRC%i_SWIZ' % idx] = src.swiz
+ fields['SRC%i_NEG' % idx] = src.neg
+ fields['SRC%i_ABS' % idx] = src.abs
+ fields['SRC%i_AMODE' % idx] = src.amode
+ fields['SRC%i_RGROUP' % idx] = src.rgroup
+
+ # XXX check for colliding fields
+ domain = isa.lookup_domain('VIV_ISA')
+ rv = [0,0,0,0]
+ for word in [0,1,2,3]:
+ mask = 0
+ bitset = domain.lookup_address(word*4)[-1][0].type
+ for field in bitset.bitfields:
+ if field.name in fields:
+ try:
+ rv[word] |= field.fill(fields[field.name])
+ del fields[field.name]
+ except ValueError,e:
+ warnings.append(str(e))
+ for field in fields.iterkeys(): # warn if fields are not used, that's probably a typo
+ warnings.append('Field %s not used' % field)
+ return rv
+
+class Assembler(object):
+ '''
+ Instruction assembler context.
+ '''
+ labels = {}
+ linenr = 0
+ instructions = None
+ source = None
+ def __init__(self, isa):
+ self.isa = isa
+ self.errors = []
+ self.instructions = []
+ self.source = []
+
+ def parse(self, line):
+ # remove comment
+ self.source.append(line)
+ self.linenr += 1
+ (line, _, _) = line.partition(';')
+
+ (label, _, line) = line.rpartition(':')
+ if label:
+ label = label.strip()
+ if not label_re.match(label):
+ self.errors.append((self.linenr, 'Invalid label: %s' % label))
+ self.labels[label] = len(self.instructions)
+
+ (inst, _, operands) = line.strip().partition(' ')
+
+ inst = inst.split('.')
+ if not inst[0]: # empty line
+ return None
+
+ try:
+ op = self.isa.types['INST_OPCODE'].values_by_name[inst[0]].value
+ except KeyError:
+ self.errors.append((self.linenr, 'Unknown instruction %s' % inst[0]))
+ return None
+
+ cond = 0
+ sat = False
+ conditions = self.isa.types['INST_CONDITION'].values_by_name
+ for atom in inst[1:]:
+ if atom in conditions:
+ cond = conditions[atom].value
+ elif atom == 'SAT':
+ sat = True
+ else:
+ self.errors.append((self.linenr, 'Unknown atom %s' % atom))
+ return None
+
+ operands = operands.split(',')
+ src = []
+ dst = None
+ tex = None
+ addr = None
+ for idx,operand in enumerate(operands):
+ operand = operand.strip()
+ neg = False
+ abs = False
+ if operand.startswith('|'):
+ if not operand.endswith('|'):
+ self.errors.append((self.linenr, 'Unterminated |'))
+ abs = True
+ operand = operand[1:-1]
+ if operand.startswith('-'):
+ neg = True
+ operand = operand[1:]
+
+ # check kind of operand
+ # (t|u|a)XXX[.xyzw] (address)register
+ match_reg = reg_re.match(operand)
+ if match_reg:
+ (regtype, regid, amode, swiz) = match_reg.groups()
+ regid = int(regid)
+ try:
+ amode = parse_amode(amode)
+ except LookupError:
+ self.errors.append((self.linenr, 'Unknown amode %s' % amode))
+ amode = 0
+ if idx == 0: # destination operand
+ comps = parse_comps(swiz)
+ if regtype == 't':
+ dst = DstOperand(use=1, amode=amode, reg=regid, comps=comps)
+ elif regtype == 'a':
+ dst = DstOperandAReg(reg=regid, comps=comps)
+ else:
+ self.errors.append((self.linenr, 'Cannot have texture or uniform as destination argument'))
+ else: # source operand
+ try:
+ swiz = parse_swiz(swiz)
+ except LookupError:
+ self.errors.append((self.linenr, 'Unknown swizzle %s' % swiz))
+ swiz = 0
+ if regtype == 't' or regtype == 'u':
+ rgroup = 0
+ if regtype == 'u':
+ if regid < 128:
+ rgroup = 2
+ else:
+ rgroup = 3
+ regid -= 128
+ src.append(SrcOperand(use=1, reg=regid, swiz=swiz, neg=neg, abs=abs, amode=amode, rgroup=rgroup))
+ elif regtype == 'a':
+ src.append(DstOperandAReg(reg=regid, comps=comps))
+ elif regtype == 'tex':
+ tex = TexOperand(id=regid, amode=amode, swiz=swiz)
+ else:
+ self.errors.append((self.linenr, 'Unparseable register type %s' % regtype))
+ arg_obj = None
+ elif operand == 'void':
+ #print('void')
+ if idx == 0: # destination operand
+ dst = None
+ else:
+ src.append(None)
+ elif label_re.match(operand):
+ #print('label ', operand)
+ addr = AddrOperand(addr = operand) # will resolve labels later
+ else:
+ self.errors.append((self.linenr, 'Unknown operand ' + operand))
+ inst_out = Instruction(op=op,
+ cond=cond,sat=sat,
+ tex=tex,dst=dst,src=src,addr=addr,unknowns={},linenr=self.linenr)
+ self.instructions.append(inst_out)
+ return inst_out
+
+ def generate_code(self):
+ rv = []
+ for inst in self.instructions:
+ warnings = []
+ if inst.addr is not None: # fill in labels
+ try:
+ addr = AddrOperand(self.labels[inst.addr.addr])
+ except LookupError:
+ self.errors.append((inst.linenr, 'Unknown label ' + inst.addr.addr))
+ inst = inst._replace(addr=addr)
+ inst_out = assemble(self.isa, inst, warnings)
+ rv.append(inst_out)
+
+ dis_i = disassemble(self.isa, inst_out, warnings)
+ if not compare_inst(inst, dis_i, warnings):
+ # Assembly did not match disassembly, print details
+ warnings.append('%08x %08x %08x %08x %s' % (
+ inst_out[0], inst_out[1], inst_out[2], inst_out[3], format_instruction(self.isa, dis_i)))
+ warnings.append(' orig : %s' % (self.source[inst.linenr-1]))
+
+ for warning in warnings:
+ self.errors.append((inst.linenr, warning))
+ return rv
+
+def compare_inst(a,b,warnings):
+ match = True
+ for attr in ['op', 'cond', 'sat', 'tex', 'dst', 'src', 'addr']:
+ if getattr(a, attr) != getattr(b, attr):
+ warnings.append('Assembly/disassembly mismatch: %s %s %s' % (attr, getattr(a, attr), getattr(b, attr)))
+ match = False
+ return match
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(description='Disassemble shader')
+ parser.add_argument('isa_file', metavar='ISAFILE', type=str,
+ help='Shader ISA definition file (rules-ng-ng)')
+ parser.add_argument('input', metavar='INFILE', type=str,
+ help='Shader assembly file')
+ #parser.add_argument('output', metavar='OUTFILE', type=str,
+ # help='Binary shader file')
+ #parser.add_argument('-a', dest='addr',
+ # default=False, action='store_const', const=True,
+ # help='Show address data with instructions')
+ parser.add_argument('-o', dest='bin_out', type=str,
+ help='Write binary shader to output file')
+ return parser.parse_args()
+
+def main():
+ args = parse_arguments()
+ out = sys.stdout
+ isa = parse_rng_file(args.isa_file)
+
+ with open(args.input, 'rb') as f:
+ asm = Assembler(isa)
+ errors = []
+ for linenr, line in enumerate(f):
+ line = line.rstrip('\n')
+ asm.parse(line)
+
+ if not asm.errors:
+ code = asm.generate_code()
+ else:
+ code = None
+
+ for line, error in asm.errors:
+ print('Line %i: %s' % (line, error))
+
+ if code is not None:
+ if args.bin_out is not None:
+ with open(args.bin_out, 'wb') as f:
+ for inst in code:
+ f.write(struct.pack(b'<IIII', *inst))
+ else: # no binary output, print as ascii
+ for inst in code:
+ print('0x%08x,0x%08x,0x%08x,0x%08x,' % tuple(inst))
+
+
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/tools/disasm.py b/tools/disasm.py
index 31d9337..2a402f7 100755
--- a/tools/disasm.py
+++ b/tools/disasm.py
@@ -31,171 +31,7 @@ from binascii import b2a_hex
from collections import namedtuple
from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain
-
-# Register groups
-# t temporary
-# u uniform 0..127
-# v uniform 127..255 (this is rewritten to u in format_src)
-# others are unknown
-rgroups = ['t', '?1?', 'u', 'v', '?4?', '?5?', '?6?', '?7?']
-# Addressing modes
-amodes = ['', 'a.x', 'a.y', 'a.z', 'a.w', '?5?', '?6?', '?7?']
-# components
-COMPS = 'xyzw'
-
-def bitextr(val, hi, lo):
- '''Extract and return bits hi..lo from value val'''
- return (val >> lo) & ((1<<(hi-lo+1))-1)
-def format_swiz(swiz):
- swiz = [(swiz >> x)&3 for x in [0,2,4,6]]
- return ''.join([COMPS[c] for c in swiz])
-def format_comps(comps):
- return ''.join([COMPS[c] for c in range(4) if ((comps >> c)&1)])
-
-DstOperand = namedtuple('DstOperand', ['use', 'amode', 'reg', 'comps'])
-SrcOperand = namedtuple('SrcOperand', ['use', 'reg', 'swiz', 'neg', 'abs', 'amode', 'rgroup'])
-TexOperand = namedtuple('TexOperand', ['id', 'amode', 'swiz'])
-Instruction = namedtuple('Instruction', ['op', 'cond', 'sat', 'tex', 'dst', 'src', 'unknowns'])
-
-def disassemble(isa, inst):
- '''Parse four 32-bit instruction words into Instruction object'''
- op = bitextr(inst[0], 5, 0)
- cond = bitextr(inst[0], 10, 6)
- sat = bitextr(inst[0], 11, 11) # saturate
-
- dst = DstOperand(
- use = bitextr(inst[0], 12, 12), # desination used
- amode = bitextr(inst[0], 15, 13), # addressing mode
- reg = bitextr(inst[0], 22, 16), # reg nr
- comps = bitextr(inst[0], 26, 23) # xyzw
- )
-
- tex = TexOperand(
- id = bitextr(inst[0], 31, 27), # texture sampler id
- amode = bitextr(inst[1], 2, 0),
- swiz = bitextr(inst[1], 10, 3)
- )
-
- src = [
- SrcOperand(
- use = bitextr(inst[1], 11, 11),
- reg = bitextr(inst[1], 20, 12),
- swiz = bitextr(inst[1], 29, 22),
- neg = bitextr(inst[1], 30, 30),
- abs = bitextr(inst[1], 31, 31),
- amode = bitextr(inst[2], 2, 0), # addressing mode
- rgroup = bitextr(inst[2], 5, 3) # reg type (0=temp, 1=?, 2=uniform, 3=uniform)
- ),
- SrcOperand(
- use = bitextr(inst[2], 6, 6),
- reg = bitextr(inst[2], 15, 7),
- swiz = bitextr(inst[2], 24, 17),
- neg = bitextr(inst[2], 25, 25),
- abs = bitextr(inst[2], 26, 26),
- amode = bitextr(inst[2], 29, 27),
- rgroup = bitextr(inst[3], 2, 0)
- ),
- SrcOperand(
- use = bitextr(inst[3], 3, 3),
- reg = bitextr(inst[3], 12, 4),
- swiz = bitextr(inst[3], 21, 14),
- neg = bitextr(inst[3], 22, 22),
- abs = bitextr(inst[3], 23, 23),
- amode = bitextr(inst[3], 27, 25),
- rgroup = bitextr(inst[3], 30, 28)
- )
- ]
-
- # Unknown fields -- these must be 0
- unknowns = [
- ('bit_1_21', bitextr(inst[1], 21, 21)),
- ('bit_2_16', bitextr(inst[2], 16, 16)),
- ('bit_2_28', bitextr(inst[2], 31, 28)),
- ('bit_3_16', bitextr(inst[3], 13, 13)),
- ('bit_3_24', bitextr(inst[3], 24, 24)),
- ('bit_3_31', bitextr(inst[3], 31, 31))
- ]
- return Instruction(op=op,cond=cond,sat=sat,tex=tex,dst=dst,src=src,unknowns=unknowns)
-
-def format_dst(isa, dst, warnings):
- '''Format destination operand'''
- if dst.use:
- # actually, target register group depends on the instruction, but usually it's a temporary...
- arg = 't%i' % (dst.reg)
- if dst.amode != 0:
- arg += '[%s]' % amodes[dst.amode]
- if dst.comps != 15: # if not all comps selected
- arg += '.' + format_comps(dst.comps)
- else:
- arg = 'void' # unused argument
- if dst.amode != 0 or dst.reg != 0 or dst.comps != 0:
- warnings.append('dst not used but fields non-zero')
-
- return arg
-
-def format_src(isa, src, warnings):
- '''Format source operand'''
- if src.use:
- if src.rgroup == 3: # map vX to uniform u(X+128)
- rgroup = 2
- reg = 128 + src.reg
- else:
- rgroup = src.rgroup
- reg = src.reg
- arg = '%s%i' % (rgroups[rgroup], reg)
- if src.amode != 0:
- arg += '[%s]' % amodes[src.amode]
- if src.swiz != 0xe4: # if not null swizzle
- arg += '.' + format_swiz(src.swiz)
- # XXX is the - or the | done first? In a way, -|x| is the only ordering that makes sense.
- if src.abs:
- return '|' + arg + '|'
- if src.neg:
- return '-' + arg
- else:
- arg = 'void' # unused argument
- if src.reg != 0 or src.swiz != 0 or src.neg != 0 or src.abs != 0 or src.amode != 0 or src.rgroup != 0:
- warnings.append('src not used but fields non-zero')
- return arg
-
-def format_tex(isa, tex, warnings):
- '''Format texture operand'''
- arg = 'tex%i' % (tex.id)
- if tex.amode != 0:
- arg += '[%i]' % amodes[tex.amode]
- if tex.swiz != 0xe4: # if not null swizzle
- arg += '.' + format_swiz(tex.swiz)
-
- return arg
-
-def format_instruction(isa, inst, warnings):
- '''
- Format instruction as text.
- '''
- atoms = []
- args = []
- atoms.append(isa.types['INST_OPCODE'].describe(inst.op))
- if inst.cond:
- atoms.append(isa.types['INST_CONDITION'].describe(inst.cond))
- if inst.sat:
- atoms.append(sat)
- opcode = '.'.join(atoms)
-
- args.append(format_dst(isa, inst.dst, warnings))
- if inst.op in [0x18, 0x19, 0x1A, 0x1B, 0x1C]:
- args.append(format_tex(isa, inst.tex, warnings))
- else:
- if inst.tex.id != 0 or inst.tex.amode != 0 or inst.tex.swiz != 0:
- warnings.append('tex not used but fields non-zero')
-
- for src in inst.src:
- args.append(format_src(isa, src, warnings))
-
- # verify that all bits in unknown are 0
- for (name,value) in inst.unknowns:
- if value != 0:
- warnings.append('!%s=%i!' % (name,value))
- return opcode+' '+(', '.join(args))
+from etnaviv.asm_common import format_instruction, disassemble
def parse_arguments():
parser = argparse.ArgumentParser(description='Disassemble shader')
@@ -203,8 +39,12 @@ def parse_arguments():
help='Shader ISA definition file (rules-ng-ng)')
parser.add_argument('input', metavar='INFILE', type=str,
help='Binary shader file')
- #parser.add_argument('-r', dest='raw_out', type=str,
- # help='Export raw data to file')
+ parser.add_argument('-a', dest='addr',
+ default=False, action='store_const', const=True,
+ help='Show address data with instructions')
+ parser.add_argument('-r', dest='raw',
+ default=False, action='store_const', const=True,
+ help='Show raw data with instructions')
return parser.parse_args()
def main():
@@ -219,9 +59,13 @@ def main():
exit(1)
for idx in xrange(len(data)//16):
inst = struct.unpack(b'<IIII', data[idx*16:idx*16+16])
- parsed = disassemble(isa, inst)
+ if args.addr:
+ out.write('%3x: ' % idx)
+ if args.raw:
+ out.write('%08x %08x %08x %08x ' % inst)
warnings = []
- text = format_instruction(isa, parsed, warnings)
+ parsed = disassemble(isa, inst, warnings)
+ text = format_instruction(isa, parsed)
out.write(text)
if warnings:
out.write(' ; ')
diff --git a/tools/etnaviv/asm_common.py b/tools/etnaviv/asm_common.py
new file mode 100644
index 0000000..de1cff8
--- /dev/null
+++ b/tools/etnaviv/asm_common.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+'''
+Etna shader disassembler/assembler common utils.
+'''
+# Copyright (c) 2012-2013 Wladimir J. van der Laan
+#
+# 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, sub license,
+# 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 NON-INFRINGEMENT. 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.
+from __future__ import print_function, division, unicode_literals
+import argparse,struct
+import sys
+from binascii import b2a_hex
+from collections import namedtuple
+
+from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain
+
+# Register groups
+# t temporary
+# u uniform 0..127
+# v uniform 127..255 (this is rewritten to u in format_src)
+# others are unknown
+RGROUPS = ['t', '?1?', 'u', 'v', '?4?', '?5?', '?6?', '?7?']
+# Addressing modes
+AMODES = ['', 'a.x', 'a.y', 'a.z', 'a.w', '?5?', '?6?', '?7?']
+# components
+COMPS = 'xyzw'
+
+def format_swiz(swiz):
+ swiz = [(swiz >> x)&3 for x in [0,2,4,6]]
+ return ''.join([COMPS[c] for c in swiz])
+def format_comps(comps):
+ return ''.join([COMPS[c] for c in range(4) if ((comps >> c)&1)])
+
+DstOperand = namedtuple('DstOperand', ['use', 'amode', 'reg', 'comps'])
+DstOperandAReg = namedtuple('DstOperandAReg', ['reg', 'comps'])
+SrcOperand = namedtuple('SrcOperand', ['use', 'reg', 'swiz', 'neg', 'abs', 'amode', 'rgroup'])
+TexOperand = namedtuple('TexOperand', ['id', 'amode', 'swiz'])
+AddrOperand = namedtuple('AddrOperand', ['addr'])
+Instruction = namedtuple('Instruction', ['op', 'cond', 'sat', 'tex', 'dst', 'src', 'addr', 'unknowns', 'linenr'])
+
+def disassemble(isa, inst, warnings):
+ '''Parse four 32-bit instruction words into Instruction object'''
+ # Extract bit fields using ISA
+ domain = isa.lookup_domain('VIV_ISA')
+ fields = {}
+ for word in [0,1,2,3]:
+ mask = 0
+ bitset = domain.lookup_address(word*4)[-1][0].type
+ for field in bitset.bitfields:
+ fields[field.name] = field.extract(inst[word])
+ mask |= field.mask
+ if mask != 0xffffffff:
+ warnings.append('isa for word %i incomplete' % word)
+ op = fields['OPCODE']
+
+ if op in [0x0A, 0x0B]: # Move to address register
+ dst = DstOperandAReg(
+ reg = fields['DST_REG'], # reg nr
+ comps = fields['DST_COMPS'] # xyzw
+ )
+ if fields['DST_AMODE'] != 0 or fields['DST_USE'] != 0:
+ warnings.append('use and amode bitfields are nonzero for areg')
+ else:
+ dst = DstOperand(
+ use = fields['DST_USE'], # destination used
+ amode = fields['DST_AMODE'], # addressing mode
+ reg = fields['DST_REG'], # reg nr
+ comps = fields['DST_COMPS'] # xyzw
+ )
+ if not dst.use:
+ if dst.amode != 0 or dst.reg != 0 or dst.comps != 0:
+ warnings.append('dst not used but fields non-zero')
+ dst = None
+
+ tex = TexOperand(
+ id = fields['TEX_ID'], # texture sampler id
+ amode = fields['TEX_AMODE'],
+ swiz = fields['TEX_SWIZ']
+ )
+ if op not in [0x18, 0x19, 0x1A, 0x1B, 0x1C]: # tex op
+ if tex.id != 0 or tex.amode != 0 or tex.swiz != 0:
+ warnings.append('tex not used but fields non-zero')
+ tex = None
+
+ if op in [0x14, 0x16]: # CALL, BRANCH
+ # Address (immediate) operand takes the place of src2
+ addr = AddrOperand(fields['SRC2_IMM'])
+ else:
+ addr = None
+
+ # Determine number of source operands
+ num_src = 3
+ if addr is not None: # src2 is invalid when address operand used
+ num_src = 2
+
+ src = []
+ for idx in xrange(num_src):
+ operand = SrcOperand(
+ use = fields['SRC%i_USE' % idx], reg = fields['SRC%i_REG' % idx],
+ swiz = fields['SRC%i_SWIZ' % idx], neg = fields['SRC%i_NEG' % idx],
+ abs = fields['SRC%i_ABS' % idx], amode = fields['SRC%i_AMODE' % idx],
+ rgroup = fields['SRC%i_RGROUP' % idx]
+ )
+ if not operand.use:
+ if operand.reg != 0 or operand.swiz != 0 or operand.neg != 0 or operand.abs != 0 or operand.amode != 0 or operand.rgroup != 0:
+ warnings.append('src%i not used but fields non-zero' % idx)
+ operand = None
+
+ src.append(operand)
+
+ # Unknown fields -- will warn if these are not 0
+ unknowns = [
+ ('bit_1_21', fields['UNK1_21']), ('bit_2_16', fields['UNK2_16']),
+ ('bit_2_30', fields['UNK2_30']), ('bit_3_24', fields['UNK3_24']),
+ ('bit_3_31', fields['UNK3_31'])
+ ]
+ if addr is None: # bit13 may be set if immediate operand 2
+ unknowns.append(('bit_3_13', fields['UNK3_13']))
+ # verify that all bits in unknown are 0
+ for (name,value) in unknowns:
+ if value != 0:
+ warnings.append('!%s=%i!' % (name,value))
+ return Instruction(op=op,
+ cond=fields['COND'],sat=fields['SAT'],
+ tex=tex,dst=dst,src=src,addr=addr,unknowns=unknowns,linenr=None)
+
+def format_dst(isa, dst):
+ '''Format destination operand'''
+ if dst is not None:
+ # actually, target register group depends on the instruction, but usually it's a temporary...
+ arg = 't%i' % (dst.reg)
+ if dst.amode != 0:
+ arg += '[%s]' % amodes[dst.amode]
+ if dst.comps != 15: # if not all comps selected
+ arg += '.' + format_comps(dst.comps)
+ else:
+ arg = 'void' # unused argument
+
+ return arg
+
+def format_dst_areg(isa, dst):
+ '''Format destination operand'''
+ arg = 'a%i' % (dst.reg)
+ if dst.comps != 15: # if not all comps selected
+ arg += '.' + format_comps(dst.comps)
+
+ return arg
+
+def format_src(isa, src):
+ '''Format source operand'''
+ if src is not None:
+ if src.rgroup == 3: # map vX to uniform u(X+128)
+ rgroup = 2
+ reg = 128 + src.reg
+ else:
+ rgroup = src.rgroup
+ reg = src.reg
+ arg = '%s%i' % (RGROUPS[rgroup], reg)
+ if src.amode != 0:
+ arg += '[%s]' % AMODES[src.amode]
+ if src.swiz != 0xe4: # if not null swizzle
+ arg += '.' + format_swiz(src.swiz)
+ # XXX is the - or the | done first? In a way, -|x| is the only ordering that makes sense.
+ if src.abs:
+ return '|' + arg + '|'
+ if src.neg:
+ return '-' + arg
+ else:
+ arg = 'void' # unused argument
+ return arg
+
+def format_tex(isa, tex):
+ '''Format texture operand'''
+ arg = 'tex%i' % (tex.id)
+ if tex.amode != 0:
+ arg += '[%i]' % amodes[tex.amode]
+ if tex.swiz != 0xe4: # if not null swizzle
+ arg += '.' + format_swiz(tex.swiz)
+
+ return arg
+
+def format_addr(isa, addr):
+ return 'label_%x' % (addr.addr)
+
+def format_instruction(isa, inst):
+ '''
+ Format instruction as text.
+ '''
+ atoms = []
+ args = []
+ atoms.append(isa.types['INST_OPCODE'].describe(inst.op))
+ if inst.cond:
+ atoms.append(isa.types['INST_CONDITION'].describe(inst.cond))
+ if inst.sat:
+ atoms.append(sat)
+ opcode = '.'.join(atoms)
+
+ if isinstance(inst.dst, DstOperandAReg):
+ args.append(format_dst_areg(isa, inst.dst))
+ else:
+ args.append(format_dst(isa, inst.dst))
+
+ if inst.tex is not None:
+ args.append(format_tex(isa, inst.tex))
+
+ for src in inst.src:
+ args.append(format_src(isa, src))
+
+ if inst.addr is not None:
+ args.append(format_addr(isa, inst.addr))
+
+ return opcode+' '+(', '.join(args))
+
diff --git a/tools/etnaviv/parse_rng.py b/tools/etnaviv/parse_rng.py
index e61e6ee..98c9d45 100644
--- a/tools/etnaviv/parse_rng.py
+++ b/tools/etnaviv/parse_rng.py
@@ -241,6 +241,13 @@ class BitField(TypedValue, RNNObject):
def extract(self, value):
'''Extract this bit field from a value'''
return (value >> self.low) & ((1<<(self.high-self.low+1))-1)
+
+ def fill(self, value):
+ '''Return value filled into this bit field'''
+ rv = (value << self.low)
+ if rv != (rv & self.mask):
+ raise ValueError('Value %i doesn\'t fit in mask %s' % (value, self.name))
+ return rv
def describe(self, value):
return self.type.describe(self.extract(value))