1Hexagon is Qualcomm's very long instruction word (VLIW) digital signal 2processor(DSP). We also support Hexagon Vector eXtensions (HVX). HVX 3is a wide vector coprocessor designed for high performance computer vision, 4image processing, machine learning, and other workloads. 5 6The following versions of the Hexagon core are supported 7 Scalar core: v67 8 https://developer.qualcomm.com/downloads/qualcomm-hexagon-v67-programmer-s-reference-manual 9 HVX extension: v66 10 https://developer.qualcomm.com/downloads/qualcomm-hexagon-v66-hvx-programmer-s-reference-manual 11 12We presented an overview of the project at the 2019 KVM Forum. 13 https://kvmforum2019.sched.com/event/Tmwc/qemu-hexagon-automatic-translation-of-the-isa-manual-pseudcode-to-tiny-code-instructions-of-a-vliw-architecture-niccolo-izzo-revng-taylor-simpson-qualcomm-innovation-center 14 15*** Tour of the code *** 16 17The qemu-hexagon implementation is a combination of qemu and the Hexagon 18architecture library (aka archlib). The three primary directories with 19Hexagon-specific code are 20 21 qemu/target/hexagon 22 This has all the instruction and packet semantics 23 qemu/target/hexagon/imported 24 These files are imported with very little modification from archlib 25 *.idef Instruction semantics definition 26 macros.def Mapping of macros to instruction attributes 27 encode*.def Encoding patterns for each instruction 28 iclass.def Instruction class definitions used to determine 29 legal VLIW slots for each instruction 30 qemu/linux-user/hexagon 31 Helpers for loading the ELF file and making Linux system calls, 32 signals, etc 33 34We start with scripts that generate a bunch of include files. This 35is a two step process. The first step is to use the C preprocessor to expand 36macros inside the architecture definition files. This is done in 37target/hexagon/gen_semantics.c. This step produces 38 <BUILD_DIR>/target/hexagon/semantics_generated.pyinc. 39That file is consumed by the following python scripts to produce the indicated 40header files in <BUILD_DIR>/target/hexagon 41 gen_opcodes_def.py -> opcodes_def_generated.h.inc 42 gen_op_regs.py -> op_regs_generated.h.inc 43 gen_printinsn.py -> printinsn_generated.h.inc 44 gen_op_attribs.py -> op_attribs_generated.h.inc 45 gen_helper_protos.py -> helper_protos_generated.h.inc 46 gen_shortcode.py -> shortcode_generated.h.inc 47 gen_tcg_funcs.py -> tcg_funcs_generated.c.inc 48 gen_tcg_func_table.py -> tcg_func_table_generated.c.inc 49 gen_helper_funcs.py -> helper_funcs_generated.c.inc 50 51Qemu helper functions have 3 parts 52 DEF_HELPER declaration indicates the signature of the helper 53 gen_helper_<NAME> will generate a TCG call to the helper function 54 The helper implementation 55 56Here's an example of the A2_add instruction. 57 Instruction tag A2_add 58 Assembly syntax "Rd32=add(Rs32,Rt32)" 59 Instruction semantics "{ RdV=RsV+RtV;}" 60 61By convention, the operands are identified by letter 62 RdV is the destination register 63 RsV, RtV are source registers 64 65The generator uses the operand naming conventions (see large comment in 66hex_common.py) to determine the signature of the helper function. Here are the 67results for A2_add 68 69helper_protos_generated.h.inc 70 DEF_HELPER_3(A2_add, s32, env, s32, s32) 71 72tcg_funcs_generated.c.inc 73 static void generate_A2_add( 74 CPUHexagonState *env, 75 DisasContext *ctx, 76 Insn *insn, 77 Packet *pkt) 78 { 79 TCGv RdV = tcg_temp_local_new(); 80 const int RdN = insn->regno[0]; 81 TCGv RsV = hex_gpr[insn->regno[1]]; 82 TCGv RtV = hex_gpr[insn->regno[2]]; 83 gen_helper_A2_add(RdV, cpu_env, RsV, RtV); 84 gen_log_reg_write(RdN, RdV); 85 ctx_log_reg_write(ctx, RdN); 86 tcg_temp_free(RdV); 87 } 88 89helper_funcs_generated.c.inc 90 int32_t HELPER(A2_add)(CPUHexagonState *env, int32_t RsV, int32_t RtV) 91 { 92 uint32_t slot __attribute__((unused)) = 4; 93 int32_t RdV = 0; 94 { RdV=RsV+RtV;} 95 return RdV; 96 } 97 98Note that generate_A2_add updates the disassembly context to be processed 99when the packet commits (see "Packet Semantics" below). 100 101The generator checks for fGEN_TCG_<tag> macro. This allows us to generate 102TCG code instead of a call to the helper. If defined, the macro takes 1 103argument. 104 C semantics (aka short code) 105 106This allows the code generator to override the auto-generated code. In some 107cases this is necessary for correct execution. We can also override for 108faster emulation. For example, calling a helper for add is more expensive 109than generating a TCG add operation. 110 111The gen_tcg.h file has any overrides. For example, we could write 112 #define fGEN_TCG_A2_add(GENHLPR, SHORTCODE) \ 113 tcg_gen_add_tl(RdV, RsV, RtV) 114 115The instruction semantics C code relies heavily on macros. In cases where the 116C semantics are specified only with macros, we can override the default with 117the short semantics option and #define the macros to generate TCG code. One 118example is L2_loadw_locked: 119 Instruction tag L2_loadw_locked 120 Assembly syntax "Rd32=memw_locked(Rs32)" 121 Instruction semantics "{ fEA_REG(RsV); fLOAD_LOCKED(1,4,u,EA,RdV) }" 122 123In gen_tcg.h, we use the shortcode 124#define fGEN_TCG_L2_loadw_locked(SHORTCODE) \ 125 SHORTCODE 126 127There are also cases where we brute force the TCG code generation. 128Instructions with multiple definitions are examples. These require special 129handling because qemu helpers can only return a single value. 130 131For HVX vectors, the generator behaves slightly differently. The wide vectors 132won't fit in a TCGv or TCGv_i64, so we pass TCGv_ptr variables to pass the 133address to helper functions. Here's an example for an HVX vector-add-word 134istruction. 135 static void generate_V6_vaddw( 136 CPUHexagonState *env, 137 DisasContext *ctx, 138 Insn *insn, 139 Packet *pkt) 140 { 141 const int VdN = insn->regno[0]; 142 const intptr_t VdV_off = 143 ctx_future_vreg_off(ctx, VdN, 1, true); 144 TCGv_ptr VdV = tcg_temp_local_new_ptr(); 145 tcg_gen_addi_ptr(VdV, cpu_env, VdV_off); 146 const int VuN = insn->regno[1]; 147 const intptr_t VuV_off = 148 vreg_src_off(ctx, VuN); 149 TCGv_ptr VuV = tcg_temp_local_new_ptr(); 150 const int VvN = insn->regno[2]; 151 const intptr_t VvV_off = 152 vreg_src_off(ctx, VvN); 153 TCGv_ptr VvV = tcg_temp_local_new_ptr(); 154 tcg_gen_addi_ptr(VuV, cpu_env, VuV_off); 155 tcg_gen_addi_ptr(VvV, cpu_env, VvV_off); 156 TCGv slot = tcg_constant_tl(insn->slot); 157 gen_helper_V6_vaddw(cpu_env, VdV, VuV, VvV, slot); 158 tcg_temp_free(slot); 159 gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false); 160 ctx_log_vreg_write(ctx, VdN, EXT_DFL, false); 161 tcg_temp_free_ptr(VdV); 162 tcg_temp_free_ptr(VuV); 163 tcg_temp_free_ptr(VvV); 164 } 165 166Notice that we also generate a variable named <operand>_off for each operand of 167the instruction. This makes it easy to override the instruction semantics with 168functions from tcg-op-gvec.h. Here's the override for this instruction. 169 #define fGEN_TCG_V6_vaddw(SHORTCODE) \ 170 tcg_gen_gvec_add(MO_32, VdV_off, VuV_off, VvV_off, \ 171 sizeof(MMVector), sizeof(MMVector)) 172 173Finally, we notice that the override doesn't use the TCGv_ptr variables, so 174we don't generate them when an override is present. Here is what we generate 175when the override is present. 176 static void generate_V6_vaddw( 177 CPUHexagonState *env, 178 DisasContext *ctx, 179 Insn *insn, 180 Packet *pkt) 181 { 182 const int VdN = insn->regno[0]; 183 const intptr_t VdV_off = 184 ctx_future_vreg_off(ctx, VdN, 1, true); 185 const int VuN = insn->regno[1]; 186 const intptr_t VuV_off = 187 vreg_src_off(ctx, VuN); 188 const int VvN = insn->regno[2]; 189 const intptr_t VvV_off = 190 vreg_src_off(ctx, VvN); 191 fGEN_TCG_V6_vaddw({ fHIDE(int i;) fVFOREACH(32, i) { VdV.w[i] = VuV.w[i] + VvV.w[i] ; } }); 192 gen_log_vreg_write(ctx, VdV_off, VdN, EXT_DFL, insn->slot, false); 193 ctx_log_vreg_write(ctx, VdN, EXT_DFL, false); 194 } 195 196In addition to instruction semantics, we use a generator to create the decode 197tree. This generation is also a two step process. The first step is to run 198target/hexagon/gen_dectree_import.c to produce 199 <BUILD_DIR>/target/hexagon/iset.py 200This file is imported by target/hexagon/dectree.py to produce 201 <BUILD_DIR>/target/hexagon/dectree_generated.h.inc 202 203*** Key Files *** 204 205cpu.h 206 207This file contains the definition of the CPUHexagonState struct. It is the 208runtime information for each thread and contains stuff like the GPR and 209predicate registers. 210 211macros.h 212mmvec/macros.h 213 214The Hexagon arch lib relies heavily on macros for the instruction semantics. 215This is a great advantage for qemu because we can override them for different 216purposes. You will also notice there are sometimes two definitions of a macro. 217The QEMU_GENERATE variable determines whether we want the macro to generate TCG 218code. If QEMU_GENERATE is not defined, we want the macro to generate vanilla 219C code that will work in the helper implementation. 220 221translate.c 222 223The functions in this file generate TCG code for a translation block. Some 224important functions in this file are 225 226 gen_start_packet - initialize the data structures for packet semantics 227 gen_commit_packet - commit the register writes, stores, etc for a packet 228 decode_and_translate_packet - disassemble a packet and generate code 229 230genptr.c 231gen_tcg.h 232 233These files create a function for each instruction. It is mostly composed of 234fGEN_TCG_<tag> definitions followed by including tcg_funcs_generated.c.inc. 235 236op_helper.c 237 238This file contains the implementations of all the helpers. There are a few 239general purpose helpers, but most of them are generated by including 240helper_funcs_generated.c.inc. There are also several helpers used for debugging. 241 242 243*** Packet Semantics *** 244 245VLIW packet semantics differ from serial semantics in that all input operands 246are read, then the operations are performed, then all the results are written. 247For exmaple, this packet performs a swap of registers r0 and r1 248 { r0 = r1; r1 = r0 } 249Note that the result is different if the instructions are executed serially. 250 251Packet semantics dictate that we defer any changes of state until the entire 252packet is committed. We record the results of each instruction in a side data 253structure, and update the visible processor state when we commit the packet. 254 255The data structures are divided between the runtime state and the translation 256context. 257 258During the TCG generation (see translate.[ch]), we use the DisasContext to 259track what needs to be done during packet commit. Here are the relevant 260fields 261 262 reg_log list of registers written 263 reg_log_idx index into ctx_reg_log 264 pred_log list of predicates written 265 pred_log_idx index into ctx_pred_log 266 store_width width of stores (indexed by slot) 267 268During runtime, the following fields in CPUHexagonState (see cpu.h) are used 269 270 new_value new value of a given register 271 reg_written boolean indicating if register was written 272 new_pred_value new value of a predicate register 273 pred_written boolean indicating if predicate was written 274 mem_log_stores record of the stores (indexed by slot) 275 276For Hexagon Vector eXtensions (HVX), the following fields are used 277 VRegs Vector registers 278 future_VRegs Registers to be stored during packet commit 279 tmp_VRegs Temporary registers *not* stored during commit 280 VRegs_updated Mask of predicated vector writes 281 QRegs Q (vector predicate) registers 282 future_QRegs Registers to be stored during packet commit 283 QRegs_updated Mask of predicated vector writes 284 285*** Debugging *** 286 287You can turn on a lot of debugging by changing the HEX_DEBUG macro to 1 in 288internal.h. This will stream a lot of information as it generates TCG and 289executes the code. 290 291To track down nasty issues with Hexagon->TCG generation, we compare the 292execution results with actual hardware running on a Hexagon Linux target. 293Run qemu with the "-d cpu" option. Then, we can diff the results and figure 294out where qemu and hardware behave differently. 295 296The stacks are located at different locations. We handle this by changing 297env->stack_adjust in translate.c. First, set this to zero and run qemu. 298Then, change env->stack_adjust to the difference between the two stack 299locations. Then rebuild qemu and run again. That will produce a very 300clean diff. 301 302Here are some handy places to set breakpoints 303 304 At the call to gen_start_packet for a given PC (note that the line number 305 might change in the future) 306 br translate.c:602 if ctx->base.pc_next == 0xdeadbeef 307 The helper function for each instruction is named helper_<TAG>, so here's 308 an example that will set a breakpoint at the start 309 br helper_A2_add 310 If you have the HEX_DEBUG macro set, the following will be useful 311 At the start of execution of a packet for a given PC 312 br helper_debug_start_packet if env->gpr[41] == 0xdeadbeef 313 At the end of execution of a packet for a given PC 314 br helper_debug_commit_end if env->this_PC == 0xdeadbeef 315