const struct v3d_qpu_instr *last;
int ip;
int last_sfu_write;
+ int last_branch_ip;
+ int last_thrsw_ip;
+
+ /* Set when we've found the last-THRSW signal, or if we were started
+ * in single-segment mode.
+ */
+ bool last_thrsw_found;
+
+ /* Set when we've found the THRSW after the last THRSW */
+ bool thrend_found;
+
+ int thrsw_count;
};
static void
abort();
}
+static bool
+in_branch_delay_slots(struct v3d_qpu_validate_state *state)
+{
+ return (state->ip - state->last_branch_ip) < 3;
+}
+
+static bool
+in_thrsw_delay_slots(struct v3d_qpu_validate_state *state)
+{
+ return (state->ip - state->last_thrsw_ip) < 3;
+}
+
static bool
qpu_magic_waddr_matches(const struct v3d_qpu_instr *inst,
bool (*predicate)(enum v3d_qpu_waddr waddr))
fail_instr(state, "LDUNIF after a LDVARY");
}
+ /* GFXH-1633 */
+ bool last_reads_ldunif = (state->last && (state->last->sig.ldunif ||
+ state->last->sig.ldunifrf));
+ bool last_reads_ldunifa = (state->last && (state->last->sig.ldunifa ||
+ state->last->sig.ldunifarf));
+ bool reads_ldunif = inst->sig.ldunif || inst->sig.ldunifrf;
+ bool reads_ldunifa = inst->sig.ldunifa || inst->sig.ldunifarf;
+ if ((last_reads_ldunif && reads_ldunifa) ||
+ (last_reads_ldunifa && reads_ldunif)) {
+ fail_instr(state,
+ "LDUNIF and LDUNIFA can't be next to each other");
+ }
+
int tmu_writes = 0;
int sfu_writes = 0;
int vpm_writes = 0;
}
}
+ if (in_thrsw_delay_slots(state)) {
+ /* There's no way you want to start SFU during the THRSW delay
+ * slots, since the result would land in the other thread.
+ */
+ if (sfu_writes) {
+ fail_instr(state,
+ "SFU write started during THRSW delay slots ");
+ }
+
+ if (inst->sig.ldvary)
+ fail_instr(state, "LDVARY during THRSW delay slots");
+ }
+
(void)qpu_magic_waddr_matches; /* XXX */
/* SFU r4 results come back two instructions later. No doing
if (sfu_writes)
state->last_sfu_write = state->ip;
+
+ if (inst->sig.thrsw) {
+ if (in_branch_delay_slots(state))
+ fail_instr(state, "THRSW in a branch delay slot.");
+
+ if (state->last_thrsw_found)
+ state->thrend_found = true;
+
+ if (state->last_thrsw_ip == state->ip - 1) {
+ /* If it's the second THRSW in a row, then it's just a
+ * last-thrsw signal.
+ */
+ if (state->last_thrsw_found)
+ fail_instr(state, "Two last-THRSW signals");
+ state->last_thrsw_found = true;
+ } else {
+ if (in_thrsw_delay_slots(state)) {
+ fail_instr(state,
+ "THRSW too close to another THRSW.");
+ }
+ state->thrsw_count++;
+ state->last_thrsw_ip = state->ip;
+ }
+ }
+
+ if (state->thrend_found &&
+ state->last_thrsw_ip - state->ip <= 2 &&
+ inst->type == V3D_QPU_INSTR_TYPE_ALU) {
+ if ((inst->alu.add.op != V3D_QPU_A_NOP &&
+ !inst->alu.add.magic_write)) {
+ fail_instr(state, "RF write after THREND");
+ }
+
+ if ((inst->alu.mul.op != V3D_QPU_M_NOP &&
+ !inst->alu.mul.magic_write)) {
+ fail_instr(state, "RF write after THREND");
+ }
+
+ if (v3d_qpu_sig_writes_address(devinfo, &inst->sig))
+ fail_instr(state, "RF write after THREND");
+
+ /* GFXH-1625: No TMUWT in the last instruction */
+ if (state->last_thrsw_ip - state->ip == 2 &&
+ inst->alu.add.op == V3D_QPU_A_TMUWT)
+ fail_instr(state, "TMUWT in last instruction");
+ }
+
+ if (inst->type == V3D_QPU_INSTR_TYPE_BRANCH) {
+ if (in_branch_delay_slots(state))
+ fail_instr(state, "branch in a branch delay slot.");
+ if (in_thrsw_delay_slots(state))
+ fail_instr(state, "branch in a THRSW delay slot.");
+ state->last_branch_ip = state->ip;
+ }
}
static void
struct v3d_qpu_validate_state state = {
.c = c,
.last_sfu_write = -10,
+ .last_thrsw_ip = -10,
+ .last_branch_ip = -10,
.ip = 0,
+
+ .last_thrsw_found = !c->last_thrsw,
};
vir_for_each_block(block, c) {
qpu_validate_block(&state, block);
}
+
+ if (state.thrsw_count > 1 && !state.last_thrsw_found) {
+ fail_instr(&state,
+ "thread switch found without last-THRSW in program");
+ }
+
+ if (!state.thrend_found)
+ fail_instr(&state, "No program-end THRSW found");
}