Path: blob/21.2-virgl/src/freedreno/afuc/emu-regs.c
4564 views
/*1* Copyright © 2021 Google, Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20* SOFTWARE.21*/2223#include <assert.h>24#include <ctype.h>25#include <stdio.h>26#include <stdlib.h>2728#include "emu.h"29#include "util.h"3031/*32* Emulator Registers:33*34* Handles access to GPR, GPU, control, and pipe registers.35*/3637static bool38is_draw_state_control_reg(unsigned n)39{40char *reg_name = afuc_control_reg_name(n);41if (!reg_name)42return false;43bool ret = !!strstr(reg_name, "DRAW_STATE");44free(reg_name);45return ret;46}4748uint32_t49emu_get_control_reg(struct emu *emu, unsigned n)50{51assert(n < ARRAY_SIZE(emu->control_regs.val));52if (is_draw_state_control_reg(n))53return emu_get_draw_state_reg(emu, n);54return emu->control_regs.val[n];55}5657void58emu_set_control_reg(struct emu *emu, unsigned n, uint32_t val)59{60EMU_CONTROL_REG(PACKET_TABLE_WRITE);61EMU_CONTROL_REG(PACKET_TABLE_WRITE_ADDR);62EMU_CONTROL_REG(REG_WRITE);63EMU_CONTROL_REG(REG_WRITE_ADDR);6465assert(n < ARRAY_SIZE(emu->control_regs.val));66BITSET_SET(emu->control_regs.written, n);67emu->control_regs.val[n] = val;6869/* Some control regs have special action on write: */70if (n == emu_reg_offset(&PACKET_TABLE_WRITE)) {71unsigned write_addr = emu_get_reg32(emu, &PACKET_TABLE_WRITE_ADDR);7273assert(write_addr < ARRAY_SIZE(emu->jmptbl));74emu->jmptbl[write_addr] = val;7576emu_set_reg32(emu, &PACKET_TABLE_WRITE_ADDR, write_addr + 1);77} else if (n == emu_reg_offset(®_WRITE)) {78uint32_t write_addr = emu_get_reg32(emu, ®_WRITE_ADDR);7980/* Upper bits seem like some flags, not part of the actual81* register offset.. not sure what they mean yet:82*/83uint32_t flags = write_addr >> 16;84write_addr &= 0xffff;8586emu_set_gpu_reg(emu, write_addr++, val);87emu_set_reg32(emu, ®_WRITE_ADDR, write_addr | (flags << 16));88} else if (is_draw_state_control_reg(n)) {89emu_set_draw_state_reg(emu, n, val);90}91}9293static uint32_t94emu_get_pipe_reg(struct emu *emu, unsigned n)95{96assert(n < ARRAY_SIZE(emu->pipe_regs.val));97return emu->pipe_regs.val[n];98}99100static void101emu_set_pipe_reg(struct emu *emu, unsigned n, uint32_t val)102{103EMU_PIPE_REG(NRT_DATA);104EMU_PIPE_REG(NRT_ADDR);105106assert(n < ARRAY_SIZE(emu->pipe_regs.val));107BITSET_SET(emu->pipe_regs.written, n);108emu->pipe_regs.val[n] = val;109110/* Some pipe regs have special action on write: */111if (n == emu_reg_offset(&NRT_DATA)) {112uintptr_t addr = emu_get_reg64(emu, &NRT_ADDR);113114emu_mem_write_dword(emu, addr, val);115116emu_set_reg64(emu, &NRT_ADDR, addr + 4);117}118}119120static uint32_t121emu_get_gpu_reg(struct emu *emu, unsigned n)122{123if (n >= ARRAY_SIZE(emu->gpu_regs.val))124return 0;125assert(n < ARRAY_SIZE(emu->gpu_regs.val));126return emu->gpu_regs.val[n];127}128129void130emu_set_gpu_reg(struct emu *emu, unsigned n, uint32_t val)131{132if (n >= ARRAY_SIZE(emu->gpu_regs.val))133return;134assert(n < ARRAY_SIZE(emu->gpu_regs.val));135BITSET_SET(emu->gpu_regs.written, n);136emu->gpu_regs.val[n] = val;137}138139static bool140is_pipe_reg_addr(unsigned regoff)141{142return regoff > 0xffff;143}144145static unsigned146get_reg_addr(struct emu *emu)147{148switch (emu->data_mode) {149case DATA_PIPE:150case DATA_ADDR: return REG_ADDR;151case DATA_USRADDR: return REG_USRADDR;152default:153unreachable("bad data_mode");154return 0;155}156}157158/* Handle reads for special streaming regs: */159static uint32_t160emu_get_fifo_reg(struct emu *emu, unsigned n)161{162/* TODO the fifo regs are slurping out of a FIFO that the hw is filling163* in parallel.. we can use `struct emu_queue` to emulate what is actually164* happening more accurately165*/166167if (n == REG_MEMDATA) {168/* $memdata */169EMU_CONTROL_REG(MEM_READ_DWORDS);170EMU_CONTROL_REG(MEM_READ_ADDR);171172unsigned read_dwords = emu_get_reg32(emu, &MEM_READ_DWORDS);173uintptr_t read_addr = emu_get_reg64(emu, &MEM_READ_ADDR);174175if (read_dwords > 0) {176emu_set_reg32(emu, &MEM_READ_DWORDS, read_dwords - 1);177emu_set_reg64(emu, &MEM_READ_ADDR, read_addr + 4);178}179180return emu_mem_read_dword(emu, read_addr);181} else if (n == REG_REGDATA) {182/* $regdata */183EMU_CONTROL_REG(REG_READ_DWORDS);184EMU_CONTROL_REG(REG_READ_ADDR);185186unsigned read_dwords = emu_get_reg32(emu, ®_READ_DWORDS);187unsigned read_addr = emu_get_reg32(emu, ®_READ_ADDR);188189/* I think if the fw doesn't write REG_READ_DWORDS before190* REG_READ_ADDR, it just ends up with a single value written191* into the FIFO that $regdata is consuming from:192*/193if (read_dwords > 0) {194emu_set_reg32(emu, ®_READ_DWORDS, read_dwords - 1);195emu_set_reg32(emu, ®_READ_ADDR, read_addr + 1);196}197198return emu_get_gpu_reg(emu, read_addr);199} else if (n == REG_DATA) {200/* $data */201do {202uint32_t rem = emu->gpr_regs.val[REG_REM];203assert(rem >= 0);204205uint32_t val;206if (emu_queue_pop(&emu->roq, &val)) {207emu_set_gpr_reg(emu, REG_REM, --rem);208return val;209}210211/* If FIFO is empty, prompt for more input: */212printf("FIFO empty, input a packet!\n");213emu->run_mode = false;214emu_main_prompt(emu);215} while (true);216} else {217unreachable("not a FIFO reg");218return 0;219}220}221222static void223emu_set_fifo_reg(struct emu *emu, unsigned n, uint32_t val)224{225if ((n == REG_ADDR) || (n == REG_USRADDR)) {226emu->data_mode = (n == REG_ADDR) ? DATA_ADDR : DATA_USRADDR;227228/* Treat these as normal register writes so we can see229* updated values in the output as we step thru the230* instructions:231*/232emu->gpr_regs.val[n] = val;233BITSET_SET(emu->gpr_regs.written, n);234235if (is_pipe_reg_addr(val)) {236/* "void" pipe regs don't have a value to write, so just237* treat it as writing zero to the pipe reg:238*/239if (afuc_pipe_reg_is_void(val >> 24))240emu_set_pipe_reg(emu, val >> 24, 0);241emu->data_mode = DATA_PIPE;242}243} else if (n == REG_DATA) {244unsigned reg = get_reg_addr(emu);245unsigned regoff = emu->gpr_regs.val[reg];246if (is_pipe_reg_addr(regoff)) {247/* writes pipe registers: */248249assert(!(regoff & 0xfbffff));250251/* If b18 is set, don't auto-increment dest addr.. and if we252* do auto-increment, we only increment the high 8b253*254* Note that we bypass emu_set_gpr_reg() in this case because255* auto-incrementing isn't triggering a write to "void" pipe256* regs.257*/258if (!(regoff & 0x40000)) {259emu->gpr_regs.val[reg] = regoff + 0x01000000;260BITSET_SET(emu->gpr_regs.written, reg);261}262263emu_set_pipe_reg(emu, regoff >> 24, val);264} else {265/* writes to gpu registers: */266emu_set_gpr_reg(emu, reg, regoff+1);267emu_set_gpu_reg(emu, regoff, val);268}269}270}271272uint32_t273emu_get_gpr_reg(struct emu *emu, unsigned n)274{275assert(n < ARRAY_SIZE(emu->gpr_regs.val));276277/* Handle special regs: */278switch (n) {279case 0x00:280return 0;281case REG_MEMDATA:282case REG_REGDATA:283case REG_DATA:284return emu_get_fifo_reg(emu, n);285default:286return emu->gpr_regs.val[n];287}288}289290void291emu_set_gpr_reg(struct emu *emu, unsigned n, uint32_t val)292{293assert(n < ARRAY_SIZE(emu->gpr_regs.val));294295switch (n) {296case REG_ADDR:297case REG_USRADDR:298case REG_DATA:299emu_set_fifo_reg(emu, n, val);300break;301default:302emu->gpr_regs.val[n] = val;303BITSET_SET(emu->gpr_regs.written, n);304break;305}306}307308/*309* Control/pipe register accessor helpers:310*/311312struct emu_reg_accessor {313unsigned (*get_offset)(const char *name);314uint32_t (*get)(struct emu *emu, unsigned n);315void (*set)(struct emu *emu, unsigned n, uint32_t val);316};317318const struct emu_reg_accessor emu_control_accessor = {319.get_offset = afuc_control_reg,320.get = emu_get_control_reg,321.set = emu_set_control_reg,322};323324const struct emu_reg_accessor emu_pipe_accessor = {325.get_offset = afuc_pipe_reg,326.get = emu_get_pipe_reg,327.set = emu_set_pipe_reg,328};329330const struct emu_reg_accessor emu_gpu_accessor = {331.get_offset = afuc_gpu_reg,332.get = emu_get_gpu_reg,333.set = emu_set_gpu_reg,334};335336unsigned337emu_reg_offset(struct emu_reg *reg)338{339if (reg->offset == ~0)340reg->offset = reg->accessor->get_offset(reg->name);341return reg->offset;342}343344uint32_t345emu_get_reg32(struct emu *emu, struct emu_reg *reg)346{347return reg->accessor->get(emu, emu_reg_offset(reg));348}349350uint64_t351emu_get_reg64(struct emu *emu, struct emu_reg *reg)352{353uint64_t val = reg->accessor->get(emu, emu_reg_offset(reg) + 1);354val <<= 32;355val |= reg->accessor->get(emu, emu_reg_offset(reg));356return val;357}358359void360emu_set_reg32(struct emu *emu, struct emu_reg *reg, uint32_t val)361{362reg->accessor->set(emu, emu_reg_offset(reg), val);363}364365void366emu_set_reg64(struct emu *emu, struct emu_reg *reg, uint64_t val)367{368reg->accessor->set(emu, emu_reg_offset(reg), val);369reg->accessor->set(emu, emu_reg_offset(reg) + 1, val >> 32);370}371372373