blob: 178c883920412de7a77342525ee18092d51cfff6 [file] [log] [blame]
/*
* Copyright (C) 2016 Etnaviv Project.
* Distributed under the MIT software license, see the accompanying
* file COPYING or http://www.opensource.org/licenses/mit-license.php.
*/
/* Verify ALU operations */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <time.h>
#include "drm_setup.h"
#include "cmdstream.h"
#include "memutil.h"
#include "state.xml.h"
#include "state_3d.xml.h"
#include "common.xml.h"
#define GPU_CODE(x) {x, ARRAY_SIZE(x)}
struct gpu_code {
const uint32_t *code;
unsigned size;
};
enum compare_type {
CT_INT32,
CT_INT32_BCAST,
CT_FLOAT32
};
struct op_test {
const char *op_name;
size_t elements_out;
enum compare_type compare_type;
void (*generate_values_h)(size_t seed, void *a, size_t width);
// Leave NULL for unary ops
void (*generate_values_v)(size_t seed, void *b, size_t height);
void (*compute_cpu)(void *out, const void *a, const void *b, size_t width, size_t height);
struct gpu_code gpu_code;
};
struct gpu_code prelude = GPU_CODE(((uint32_t[]){
0x00821019, 0x00200800, 0x80010000, 0x203fc008, /* lshift.u32 t2.x___, t0.xxxx, void, u0.wwww */
0x07821032, 0x15600800, 0x80000150, 0x00000000, /* load.u32 t2, u0.yyyy, t2.xxxx, void */
0x00831019, 0x15600800, 0x80010000, 0x203fc008, /* lshift.u32 t3.x___, t0.yyyy, void, u0.wwww */
0x07831032, 0x2aa00800, 0x800001d0, 0x00000000, /* load.u32 t3, u0.zzzz, t3.xxxx, void */
0x07841009, 0x00000000, 0x00000000, 0x20390028, /* mov t4, void, void, u2 */
}));
struct gpu_code postlude = GPU_CODE(((uint32_t[]){
0x0080100c, 0x15600800, 0x800100c0, 0x0000000a, /* imadlo0.u32 t0.x___, t0.yyyy, u1.xxxx, t0.xxxx */
0x00801019, 0x00200800, 0x80010000, 0x203fc008, /* lshift.u32 t0.x___, t0.xxxx, void, u0.wwww */
0x07800033, 0x00200800, 0x80000050, 0x00390048, /* store.u32 mem.xyzw, u0.xxxx, t0.xxxx, t4 */
0x00000000, 0x00000000, 0x00000000, 0x00000000, /* nop void, void, void, void */
}));
static const char *COMPS = "xyzw";
#define MAX_INST 1024
static void gen_cmd_stream(struct etna_cmd_stream *stream, struct gpu_code *gpu_code, struct etna_bo *out, struct etna_bo *in0, struct etna_bo *in1)
{
unsigned num_inst;
uint32_t code[MAX_INST*4];
unsigned code_ptr = 0;
for (unsigned i=0; i<prelude.size; ++i)
code[code_ptr++] = prelude.code[i];
for (unsigned i=0; i<gpu_code->size; ++i)
code[code_ptr++] = gpu_code->code[i];
for (unsigned i=0; i<postlude.size; ++i)
code[code_ptr++] = postlude.code[i];
assert((code_ptr & 3)==0);
num_inst = code_ptr / 4; /* number of instructions including final nop */
etna_set_state(stream, VIVS_PA_SYSTEM_MODE, VIVS_PA_SYSTEM_MODE_UNK0 | VIVS_PA_SYSTEM_MODE_UNK4);
etna_set_state(stream, VIVS_GL_API_MODE, VIVS_GL_API_MODE_OPENCL);
etna_set_state_from_bo(stream, VIVS_VS_UNIFORMS(0), out); /* u0.x */
etna_set_state_from_bo(stream, VIVS_VS_UNIFORMS(1), in0); /* u0.y */
etna_set_state_from_bo(stream, VIVS_VS_UNIFORMS(2), in1); /* u0.z */
etna_set_state(stream, VIVS_VS_UNIFORMS(3), 0x4); /* u0.w Left-shift */
etna_set_state(stream, VIVS_VS_UNIFORMS(4), 0x10); /* u1.x Row stride */
etna_set_state(stream, VIVS_VS_UNIFORMS(5), 0x0); /* u1.y Unused */
etna_set_state(stream, VIVS_VS_UNIFORMS(6), 0x0); /* u1.z Unused */
etna_set_state(stream, VIVS_VS_UNIFORMS(7), 0x0); /* u1.w Unused */
etna_set_state(stream, VIVS_VS_UNIFORMS(8), 0xaaaaaaaa); /* u2.x Default output (if GPU program generates no output in t4) */
etna_set_state(stream, VIVS_VS_UNIFORMS(9), 0x55555555); /* u2.y */
etna_set_state(stream, VIVS_VS_UNIFORMS(10), 0xaaaaaaaa); /* u2.z */
etna_set_state(stream, VIVS_VS_UNIFORMS(11), 0x55555555); /* u2.w */
for (unsigned i=0; i<code_ptr; ++i)
etna_set_state(stream, VIVS_SH_INST_MEM(i), code[i]);
etna_set_state(stream, VIVS_VS_INPUT_COUNT, VIVS_VS_INPUT_COUNT_COUNT(1) | VIVS_VS_INPUT_COUNT_UNK8(1));
etna_set_state(stream, VIVS_VS_TEMP_REGISTER_CONTROL, VIVS_VS_TEMP_REGISTER_CONTROL_NUM_TEMPS(10));
etna_set_state(stream, VIVS_VS_OUTPUT(0), VIVS_VS_OUTPUT_O0(0) | VIVS_VS_OUTPUT_O1(0) | VIVS_VS_OUTPUT_O2(0) | VIVS_VS_OUTPUT_O3(0));
etna_set_state(stream, VIVS_VS_NEW_UNK00860, 0x0);
etna_set_state(stream, VIVS_VS_RANGE, VIVS_VS_RANGE_LOW(0x0) | VIVS_VS_RANGE_HIGH(num_inst - 2));
etna_set_state(stream, VIVS_VS_LOAD_BALANCING, VIVS_VS_LOAD_BALANCING_A(0x42) | VIVS_VS_LOAD_BALANCING_B(0x5) | VIVS_VS_LOAD_BALANCING_C(0x3f) | VIVS_VS_LOAD_BALANCING_D(0xf));
etna_set_state(stream, VIVS_VS_OUTPUT_COUNT, 1);
etna_set_state(stream, VIVS_GL_VARYING_NUM_COMPONENTS, VIVS_GL_VARYING_NUM_COMPONENTS_VAR0(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR1(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR2(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR3(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR4(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR5(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR6(0x0) | VIVS_GL_VARYING_NUM_COMPONENTS_VAR7(0x0));
etna_set_state(stream, VIVS_GL_UNK03834, 0x0);
etna_set_state(stream, VIVS_GL_VARYING_TOTAL_COMPONENTS, VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM(0x0));
etna_set_state(stream, VIVS_PS_INPUT_COUNT, VIVS_PS_INPUT_COUNT_COUNT(1) | VIVS_PS_INPUT_COUNT_UNK8(31));
etna_set_state(stream, VIVS_PS_TEMP_REGISTER_CONTROL, VIVS_PS_TEMP_REGISTER_CONTROL_NUM_TEMPS(10));
etna_set_state(stream, VIVS_PS_CONTROL, 0);
etna_set_state(stream, VIVS_PS_UNK01030, 0x0);
etna_set_state(stream, VIVS_PA_ATTRIBUTE_ELEMENT_COUNT, VIVS_PA_ATTRIBUTE_ELEMENT_COUNT_UNK0(0x0) | VIVS_PA_ATTRIBUTE_ELEMENT_COUNT_COUNT(0x0));
etna_set_state(stream, VIVS_CL_UNK00924, 0x0);
etna_set_state(stream, VIVS_CL_CONFIG, VIVS_CL_CONFIG_DIMENSIONS(0x2) | VIVS_CL_CONFIG_TRAVERSE_ORDER(0x0) | VIVS_CL_CONFIG_SWATH_SIZE_X(0x0) | VIVS_CL_CONFIG_SWATH_SIZE_Y(0x0) | VIVS_CL_CONFIG_SWATH_SIZE_Z(0x0) | VIVS_CL_CONFIG_VALUE_ORDER(0x3));
etna_set_state(stream, VIVS_CL_GLOBAL_X, VIVS_CL_GLOBAL_X_SIZE(0x10) | VIVS_CL_GLOBAL_X_OFFSET(0x0));
etna_set_state(stream, VIVS_CL_GLOBAL_Y, VIVS_CL_GLOBAL_Y_SIZE(0x10) | VIVS_CL_GLOBAL_Y_OFFSET(0x0));
etna_set_state(stream, VIVS_CL_GLOBAL_Z, VIVS_CL_GLOBAL_Z_SIZE(0x0) | VIVS_CL_GLOBAL_Z_OFFSET(0x0));
etna_set_state(stream, VIVS_CL_WORKGROUP_X, VIVS_CL_WORKGROUP_X_SIZE(0x7) | VIVS_CL_WORKGROUP_X_COUNT(0x1));
etna_set_state(stream, VIVS_CL_WORKGROUP_Y, VIVS_CL_WORKGROUP_Y_SIZE(0x7) | VIVS_CL_WORKGROUP_Y_COUNT(0x1));
etna_set_state(stream, VIVS_CL_WORKGROUP_Z, VIVS_CL_WORKGROUP_Z_SIZE(0x3ff) | VIVS_CL_WORKGROUP_Z_COUNT(0xffff));
etna_set_state(stream, VIVS_CL_THREAD_ALLOCATION, 0x4);
/* Kick off program */
etna_set_state(stream, VIVS_CL_KICKER, 0xbadabeeb);
/* Flush caches so that we can see the output */
etna_set_state(stream, VIVS_GL_FLUSH_CACHE, VIVS_GL_FLUSH_CACHE_TEXTURE | VIVS_GL_FLUSH_CACHE_SHADER_L1);
}
void i32_generate_values_h(size_t seed, void *a, size_t width)
{
uint32_t base = seed * width;
for (size_t x=0; x<width; ++x) {
((uint32_t*)a)[x*4+0] = base + x;
((uint32_t*)a)[x*4+1] = 0x51515151; /* fill other vector elements with recognizable random pattern */
((uint32_t*)a)[x*4+2] = 0x15151515;
((uint32_t*)a)[x*4+3] = 0x36363636;
}
}
void i32_generate_values_v(size_t seed, void *b, size_t height)
{
uint32_t base = seed * height;
for (size_t y=0; y<height; ++y) {
((uint32_t*)b)[y*4+0] = base + y;
((uint32_t*)b)[y*4+1] = 0x82828282; /* fill other vector elements with recognizable random pattern */
((uint32_t*)b)[y*4+2] = 0x48484848;
((uint32_t*)b)[y*4+3] = 0x27272727;
}
}
#define A (a[x*4])
#define B (b[y*4])
/* Scalar computations broadcasted to all channels */
#define CPU_COMPUTE_FUNC1(_name, _type, _expr) \
static void _name(_type *out, const _type *a, const _type *b, size_t width, size_t height) \
{ \
for(size_t y=0; y<height; ++y) { \
for(size_t x=0; x<width; ++x) { \
out[(y*width+x)*4+0] = out[(y*width+x)*4+1] = out[(y*width+x)*4+2] = out[(y*width+x)*4+3] = (_expr); \
} \
} \
}
/* Independent expressions for channels */
#define CPU_COMPUTE_FUNC4(_name, _type, _expr0, _expr1, _expr2, _expr3) \
static void _name(_type *out, const _type *a, const _type *b, size_t width, size_t height) \
{ \
for(size_t y=0; y<height; ++y) { \
for(size_t x=0; x<width; ++x) { \
out[(y*width+x)*4+0] = (_expr0); \
out[(y*width+x)*4+1] = (_expr1); \
out[(y*width+x)*4+2] = (_expr2); \
out[(y*width+x)*4+3] = (_expr3); \
} \
} \
}
CPU_COMPUTE_FUNC4(nop_compute_cpu, uint32_t, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555);
/* u32 */
CPU_COMPUTE_FUNC1(addu32_compute_cpu, uint32_t, A + B);
CPU_COMPUTE_FUNC1(mulu32_compute_cpu, uint32_t, A * B);
CPU_COMPUTE_FUNC1(mulhu32_compute_cpu, uint32_t, ((uint64_t)A * (uint64_t)B)>>32);
CPU_COMPUTE_FUNC1(lshiftu32_compute_cpu, uint32_t, A << (B&31));
CPU_COMPUTE_FUNC1(rshiftu32_compute_cpu, uint32_t, A >> (B&31));
CPU_COMPUTE_FUNC1(rotateu32_compute_cpu, uint32_t, (A << (B&31)) | (A >> ((32-B)&31)));
CPU_COMPUTE_FUNC1(oru32_compute_cpu, uint32_t, A | B);
CPU_COMPUTE_FUNC1(andu32_compute_cpu, uint32_t, A & B);
CPU_COMPUTE_FUNC1(xoru32_compute_cpu, uint32_t, A ^ B);
CPU_COMPUTE_FUNC1(notu32_compute_cpu, uint32_t, ~A);
CPU_COMPUTE_FUNC1(leadzerou32_compute_cpu, uint32_t, (A != 0) ? __builtin_clz(A) : 0x20);
/* float */
CPU_COMPUTE_FUNC1(addf32_compute_cpu, float, A + B);
CPU_COMPUTE_FUNC1(mulf32_compute_cpu, float, A * B);
#undef A
#undef B
#undef CPU_COMPUTE
/* Tests GPU code must take from a[x] t2 and b[y] t3, and output to t4 */
struct op_test op_tests[] = {
{"nop", 4, CT_INT32, i32_generate_values_h, i32_generate_values_v, (void*)nop_compute_cpu,
GPU_CODE(((uint32_t[]){
0x00000000, 0x00000000, 0x00000000, 0x00000000, /* nop */
}))
},
/* Add will only output one element at a time */
{"add.u32", 1, CT_INT32, i32_generate_values_h, i32_generate_values_v, (void*)addu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x00841001, 0x00202800, 0x80000000, 0x00000038, /* add.u32 t4, t2, void, t3 */
}))
},
{"add4.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)addu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x00841001, 0x00202800, 0x80000000, 0x00000038, /* add.u32 t4.x___, t2.xxxx, void, t3.xxxx */
0x01041001, 0x00202800, 0x80000000, 0x00000038, /* add.u32 t4._y__, t2.xxxx, void, t3.xxxx */
0x02041001, 0x00202800, 0x80000000, 0x00000038, /* add.u32 t4.__z_, t2.xxxx, void, t3.xxxx */
0x04041001, 0x00202800, 0x80000000, 0x00000038, /* add.u32 t4.___w, t2.xxxx, void, t3.xxxx */
}))
},
{"imullo0.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)mulu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784103c, 0x39202800, 0x81c801c0, 0x00000000, /* imullo0.u32 t4, t2, t3, void */
}))
},
{"imulhi0.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)mulhu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x07841000, 0x39202800, 0x81c901c0, 0x00000000, /* imulhi0.u32 t4, t2, t3, void */
}))
},
{"lshift.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)lshiftu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x07841019, 0x39202800, 0x80010000, 0x00390038, /* lshift.u32 t4, t2, void, t3 */
}))
},
{"rshift.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)rshiftu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784101a, 0x39202800, 0x80010000, 0x00390038, /* rshift.u32 t4, t2, void, t3 */
}))
},
{"rotate.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)rotateu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784101b, 0x39202800, 0x80010000, 0x00390038, /* rotate.u32 t4, t2, void, t3 */
}))
},
{"or.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)oru32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784101c, 0x39202800, 0x80010000, 0x00390038, /* or.u32 t4, t2, void, t3 */
}))
},
{"and.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)andu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784101d, 0x39202800, 0x80010000, 0x00390038, /* and.u32 t4, t2, void, t3 */
}))
},
{"xor.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)xoru32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784101e, 0x39202800, 0x80010000, 0x00390038, /* xor.u32 t4, t2, void, t3 */
}))
},
{"not.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)notu32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x0784101f, 0x00200000, 0x80010000, 0x00390028, /* not.u32 t4, void, void, t2 */
}))
},
{"leadzero.u32", 4, CT_INT32_BCAST, i32_generate_values_h, i32_generate_values_v, (void*)leadzerou32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x07841018, 0x00200000, 0x80010000, 0x00390028, /* leadzero.u32 t4, void, void, t2 */
}))
},
// add.u16 does nothing
// 0x00801001, 0x15402800, 0xc0000000, 0x00000018, /* add.u16 t0.x___, t2.yyyy, void, t1.xxxx */
#if 0
// Need an effective way of comparing these
{"add.f32", 4, CT_FLOAT32, i32_generate_values_h, i32_generate_values_v, (void*)addf32_compute_cpu,
GPU_CODE(((uint32_t[]){
0x00801001, 0x15402800, 0x00000000, 0x00000018, /* add.f32 t0.x___, t2.yyyy, void, t1.xxxx */
}))
},
#endif
};
int perform_test(struct drm_test_info *info, struct op_test *cur_test, int repeats)
{
int retval = -1;
const size_t unit_size = 16; /* vec4 of any 32-bit type */
const size_t width = 16;
const size_t height = 16;
size_t seedx, seedy;
struct etna_bo *bo_out=0, *bo_in0=0, *bo_in1=0;
unsigned int errors = 0;
size_t out_size = width * height * unit_size;
size_t in0_size = width * unit_size;
size_t in1_size = height * unit_size;
void *out_cpu = malloc(out_size);
void *a_cpu = malloc(in0_size);
void *b_cpu = malloc(in1_size);
memset(out_cpu, 0, out_size);
memset(a_cpu, 0, in0_size);
memset(b_cpu, 0, in1_size);
printf("%s: ", cur_test->op_name);
fflush(stdout);
bo_out = etna_bo_new(info->dev, out_size, DRM_ETNA_GEM_CACHE_UNCACHED);
bo_in0 = etna_bo_new(info->dev, in0_size, DRM_ETNA_GEM_CACHE_UNCACHED);
bo_in1 = etna_bo_new(info->dev, in1_size, DRM_ETNA_GEM_CACHE_UNCACHED);
if (!bo_in0 || !bo_in1 || !bo_out) {
fprintf(stderr, "Unable to allocate buffer\n");
goto out;
}
for (int num_tries=0; num_tries<repeats && !errors; ++num_tries) {
seedx = rand();
seedy = rand();
cur_test->generate_values_h(seedx, a_cpu, width);
cur_test->generate_values_v(seedy, b_cpu, height);
cur_test->compute_cpu(out_cpu, a_cpu, b_cpu, width, height);
memset(etna_bo_map(bo_out), 0, out_size);
memcpy(etna_bo_map(bo_in0), a_cpu, in0_size);
memcpy(etna_bo_map(bo_in1), b_cpu, in1_size);
/* generate command sequence */
gen_cmd_stream(info->stream, &cur_test->gpu_code, bo_out, bo_in0, bo_in1);
/* execute command sequence */
etna_cmd_stream_finish(info->stream);
/* verify result */
const uint32_t *out_gpu = etna_bo_map(bo_out);
if (cur_test->compare_type == CT_INT32 || cur_test->compare_type == CT_INT32_BCAST) {
for(size_t y=0; y<height; ++y) {
for(size_t x=0; x<width; ++x) {
for(size_t c=0; c<cur_test->elements_out; ++c) {
uint32_t expected = ((uint32_t*)out_cpu)[(y*width+x)*4 + c];
uint32_t found = ((uint32_t*)out_gpu)[(y*width+x)*4 + c];
if (expected != found) {
int sc = cur_test->compare_type == CT_INT32_BCAST ? 0 : c; /* source component */
if (errors < 10)
printf("Mismatch %s(%08x,%08x).%c -> %08x, expected %08x\n", cur_test->op_name,
((uint32_t*)a_cpu)[x*4 + sc], ((uint32_t*)b_cpu)[y*4 + sc],
COMPS[c], found, expected);
errors += 1;
}
}
}
}
} else {
errors = 1;
printf("No comparison implemented for num_elements %d compare_type %d\n", (int)cur_test->elements_out, cur_test->compare_type);
}
}
if (errors == 0) {
printf("PASS\n");
retval = 0;
} else {
printf("FAIL (seedx %u seedy %u)\n", (unsigned)seedx, (unsigned)seedy);
retval = 1;
}
out:
etna_bo_del(bo_out);
etna_bo_del(bo_in0);
etna_bo_del(bo_in1);
free(out_cpu);
free(a_cpu);
free(b_cpu);
return retval;
}
int main(int argc, char *argv[])
{
srand(time(NULL));
struct drm_test_info *info;
if ((info = drm_test_setup(argc, argv)) == NULL) {
return 1;
}
for (unsigned t=0; t<ARRAY_SIZE(op_tests); ++t)
{
perform_test(info, &op_tests[t], 100);
}
drm_test_teardown(info);
return 0;
drm_test_teardown(info);
return 1;
}