broadcom/vc5: Add support for V3Dv4 signal bits.
[mesa.git] / src / broadcom / compiler / qpu_validate.c
1 /*
2 * Copyright © 2014 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /**
25 * @file
26 *
27 * Validates the QPU instruction sequence after register allocation and
28 * scheduling.
29 */
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include "v3d_compiler.h"
35 #include "qpu/qpu_disasm.h"
36
37 struct v3d_qpu_validate_state {
38 struct v3d_compile *c;
39 const struct v3d_qpu_instr *last;
40 int ip;
41 int last_sfu_write;
42 };
43
44 static void
45 fail_instr(struct v3d_qpu_validate_state *state, const char *msg)
46 {
47 struct v3d_compile *c = state->c;
48
49 fprintf(stderr, "v3d_qpu_validate at ip %d: %s:\n", state->ip, msg);
50
51 int dump_ip = 0;
52 vir_for_each_inst_inorder(inst, c) {
53 v3d_qpu_dump(c->devinfo, &inst->qpu);
54
55 if (dump_ip++ == state->ip)
56 fprintf(stderr, " *** ERROR ***");
57
58 fprintf(stderr, "\n");
59 }
60
61 fprintf(stderr, "\n");
62 abort();
63 }
64
65 static bool
66 qpu_magic_waddr_matches(const struct v3d_qpu_instr *inst,
67 bool (*predicate)(enum v3d_qpu_waddr waddr))
68 {
69 if (inst->type == V3D_QPU_INSTR_TYPE_ALU)
70 return false;
71
72 if (inst->alu.add.op != V3D_QPU_A_NOP &&
73 inst->alu.add.magic_write &&
74 predicate(inst->alu.add.waddr))
75 return true;
76
77 if (inst->alu.mul.op != V3D_QPU_M_NOP &&
78 inst->alu.mul.magic_write &&
79 predicate(inst->alu.mul.waddr))
80 return true;
81
82 return false;
83 }
84
85 static void
86 qpu_validate_inst(struct v3d_qpu_validate_state *state, struct qinst *qinst)
87 {
88 const struct v3d_device_info *devinfo = state->c->devinfo;
89 const struct v3d_qpu_instr *inst = &qinst->qpu;
90
91 if (inst->type != V3D_QPU_INSTR_TYPE_ALU)
92 return;
93
94 /* LDVARY writes r5 two instructions later and LDUNIF writes
95 * r5 one instruction later, which is illegal to have
96 * together.
97 */
98 if (state->last && state->last->sig.ldvary &&
99 (inst->sig.ldunif || inst->sig.ldunifa)) {
100 fail_instr(state, "LDUNIF after a LDVARY");
101 }
102
103 int tmu_writes = 0;
104 int sfu_writes = 0;
105 int vpm_writes = 0;
106 int tlb_writes = 0;
107 int tsy_writes = 0;
108
109 if (inst->alu.add.op != V3D_QPU_A_NOP) {
110 if (inst->alu.add.magic_write) {
111 if (v3d_qpu_magic_waddr_is_tmu(inst->alu.add.waddr))
112 tmu_writes++;
113 if (v3d_qpu_magic_waddr_is_sfu(inst->alu.add.waddr))
114 sfu_writes++;
115 if (v3d_qpu_magic_waddr_is_vpm(inst->alu.add.waddr))
116 vpm_writes++;
117 if (v3d_qpu_magic_waddr_is_tlb(inst->alu.add.waddr))
118 tlb_writes++;
119 if (v3d_qpu_magic_waddr_is_tsy(inst->alu.add.waddr))
120 tsy_writes++;
121 }
122 }
123
124 if (inst->alu.mul.op != V3D_QPU_M_NOP) {
125 if (inst->alu.mul.magic_write) {
126 if (v3d_qpu_magic_waddr_is_tmu(inst->alu.mul.waddr))
127 tmu_writes++;
128 if (v3d_qpu_magic_waddr_is_sfu(inst->alu.mul.waddr))
129 sfu_writes++;
130 if (v3d_qpu_magic_waddr_is_vpm(inst->alu.mul.waddr))
131 vpm_writes++;
132 if (v3d_qpu_magic_waddr_is_tlb(inst->alu.mul.waddr))
133 tlb_writes++;
134 if (v3d_qpu_magic_waddr_is_tsy(inst->alu.mul.waddr))
135 tsy_writes++;
136 }
137 }
138
139 (void)qpu_magic_waddr_matches; /* XXX */
140
141 /* SFU r4 results come back two instructions later. No doing
142 * r4 read/writes or other SFU lookups until it's done.
143 */
144 if (state->ip - state->last_sfu_write < 2) {
145 if (v3d_qpu_uses_mux(inst, V3D_QPU_MUX_R4))
146 fail_instr(state, "R4 read too soon after SFU");
147
148 if (v3d_qpu_writes_r4(devinfo, inst))
149 fail_instr(state, "R4 write too soon after SFU");
150
151 if (sfu_writes)
152 fail_instr(state, "SFU write too soon after SFU");
153 }
154
155 /* XXX: The docs say VPM can happen with the others, but the simulator
156 * disagrees.
157 */
158 if (tmu_writes +
159 sfu_writes +
160 vpm_writes +
161 tlb_writes +
162 tsy_writes +
163 inst->sig.ldtmu +
164 inst->sig.ldtlb +
165 inst->sig.ldvpm +
166 inst->sig.ldtlbu > 1) {
167 fail_instr(state,
168 "Only one of [TMU, SFU, TSY, TLB read, VPM] allowed");
169 }
170
171 if (sfu_writes)
172 state->last_sfu_write = state->ip;
173 }
174
175 static void
176 qpu_validate_block(struct v3d_qpu_validate_state *state, struct qblock *block)
177 {
178 vir_for_each_inst(qinst, block) {
179 qpu_validate_inst(state, qinst);
180
181 state->last = &qinst->qpu;
182 state->ip++;
183 }
184 }
185
186 /**
187 * Checks for the instruction restrictions from page 37 ("Summary of
188 * Instruction Restrictions").
189 */
190 void
191 qpu_validate(struct v3d_compile *c)
192 {
193 /* We don't want to do validation in release builds, but we want to
194 * keep compiling the validation code to make sure it doesn't get
195 * broken.
196 */
197 #ifndef DEBUG
198 return;
199 #endif
200
201 struct v3d_qpu_validate_state state = {
202 .c = c,
203 .last_sfu_write = -10,
204 .ip = 0,
205 };
206
207 vir_for_each_block(block, c) {
208 qpu_validate_block(&state, block);
209 }
210 }