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