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_printinsn.py -> printinsn_generated.h.inc
47 gen_op_attribs.py -> op_attribs_generated.h.inc
48 gen_helper_protos.py -> helper_protos_generated.h.inc
49 gen_tcg_funcs.py -> tcg_funcs_generated.c.inc
50 gen_tcg_func_table.py -> tcg_func_table_generated.c.inc
51 gen_helper_funcs.py -> helper_funcs_generated.c.inc
52 gen_idef_parser_funcs.py -> idef_parser_input.h
53 gen_analyze_funcs.py -> analyze_funcs_generated.c.inc
54
55Qemu helper functions have 3 parts
56 DEF_HELPER declaration indicates the signature of the helper
57 gen_helper_<NAME> will generate a TCG call to the helper function
58 The helper implementation
59
60Here's an example of the A2_add instruction.
61 Instruction tag A2_add
62 Assembly syntax "Rd32=add(Rs32,Rt32)"
63 Instruction semantics "{ RdV=RsV+RtV;}"
64
65By convention, the operands are identified by letter
66 RdV is the destination register
67 RsV, RtV are source registers
68
69The generator uses the operand naming conventions (see large comment in
70hex_common.py) to determine the signature of the helper function. Here are the
71results for A2_add
72
73helper_protos_generated.h.inc
74 DEF_HELPER_3(A2_add, s32, env, s32, s32)
75
76tcg_funcs_generated.c.inc
77 static void generate_A2_add(
78 CPUHexagonState *env,
79 DisasContext *ctx,
80 Insn *insn,
81 Packet *pkt)
82 {
83 TCGv RdV = tcg_temp_new();
84 const int RdN = insn->regno[0];
85 TCGv RsV = hex_gpr[insn->regno[1]];
86 TCGv RtV = hex_gpr[insn->regno[2]];
87 gen_helper_A2_add(RdV, tcg_env, RsV, RtV);
88 gen_log_reg_write(ctx, RdN, RdV);
89 }
90
91helper_funcs_generated.c.inc
92 int32_t HELPER(A2_add)(CPUHexagonState *env, int32_t RsV, int32_t RtV)
93 {
94 uint32_t slot __attribute__((unused)) = 4;
95 int32_t RdV = 0;
96 { RdV=RsV+RtV;}
97 return RdV;
98 }
99
100Note that generate_A2_add updates the disassembly context to be processed
101when the packet commits (see "Packet Semantics" below).
102
103The generator checks for fGEN_TCG_<tag> macro. This allows us to generate
104TCG code instead of a call to the helper. If defined, the macro takes 1
105argument.
106 C semantics (aka short code)
107
108This allows the code generator to override the auto-generated code. In some
109cases this is necessary for correct execution. We can also override for
110faster emulation. For example, calling a helper for add is more expensive
111than generating a TCG add operation.
112
113The gen_tcg.h file has any overrides. For example, we could write
114 #define fGEN_TCG_A2_add(GENHLPR, SHORTCODE) \
115 tcg_gen_add_tl(RdV, RsV, RtV)
116
117The instruction semantics C code relies heavily on macros. In cases where the
118C semantics are specified only with macros, we can override the default with
119the short semantics option and #define the macros to generate TCG code. One
120example is L2_loadw_locked:
121 Instruction tag L2_loadw_locked
122 Assembly syntax "Rd32=memw_locked(Rs32)"
123 Instruction semantics "{ fEA_REG(RsV); fLOAD_LOCKED(1,4,u,EA,RdV) }"
124
125In gen_tcg.h, we use the shortcode
126#define fGEN_TCG_L2_loadw_locked(SHORTCODE) \
127 SHORTCODE
128
129There are also cases where we brute force the TCG code generation.
130Instructions with multiple definitions are examples. These require special
131handling because qemu helpers can only return a single value.
132
133For HVX vectors, the generator behaves slightly differently. The wide vectors
134won't fit in a TCGv or TCGv_i64, so we pass TCGv_ptr variables to pass the
135address to helper functions. Here's an example for an HVX vector-add-word
136istruction.
137 static void generate_V6_vaddw(DisasContext *ctx)
138 {
139 Insn *insn __attribute__((unused)) = ctx->insn;
140 const int VdN = insn->regno[0];
141 const intptr_t VdV_off =
142 ctx_future_vreg_off(ctx, VdN, 1, true);
143 TCGv_ptr VdV = tcg_temp_new_ptr();
144 tcg_gen_addi_ptr(VdV, tcg_env, VdV_off);
145 const int VuN = insn->regno[1];
146 const intptr_t VuV_off =
147 vreg_src_off(ctx, VuN);
148 TCGv_ptr VuV = tcg_temp_new_ptr();
149 const int VvN = insn->regno[2];
150 const intptr_t VvV_off =
151 vreg_src_off(ctx, VvN);
152 TCGv_ptr VvV = tcg_temp_new_ptr();
153 tcg_gen_addi_ptr(VuV, tcg_env, VuV_off);
154 tcg_gen_addi_ptr(VvV, tcg_env, VvV_off);
155 gen_helper_V6_vaddw(tcg_env, VdV, VuV, VvV);
156 }
157
158Notice that we also generate a variable named <operand>_off for each operand of
159the instruction. This makes it easy to override the instruction semantics with
160functions from tcg-op-gvec.h. Here's the override for this instruction.
161 #define fGEN_TCG_V6_vaddw(SHORTCODE) \
162 tcg_gen_gvec_add(MO_32, VdV_off, VuV_off, VvV_off, \
163 sizeof(MMVector), sizeof(MMVector))
164
165Finally, we notice that the override doesn't use the TCGv_ptr variables, so
166we don't generate them when an override is present. Here is what we generate
167when the override is present.
168 static void generate_V6_vaddw(DisasContext *ctx)
169 {
170 Insn *insn __attribute__((unused)) = ctx->insn;
171 const int VdN = insn->regno[0];
172 const intptr_t VdV_off =
173 ctx_future_vreg_off(ctx, VdN, 1, true);
174 const int VuN = insn->regno[1];
175 const intptr_t VuV_off =
176 vreg_src_off(ctx, VuN);
177 const int VvN = insn->regno[2];
178 const intptr_t VvV_off =
179 vreg_src_off(ctx, VvN);
180 fGEN_TCG_V6_vaddw({ fHIDE(int i;) fVFOREACH(32, i) { VdV.w[i] = VuV.w[i] + VvV.w[i] ; } });
181 }
182
183We also generate an analyze_<tag> function for each instruction. Currently,
184these functions record the reads and writes to registers by calling ctx_log_*.
185During gen_start_packet, we invoke the analyze_<tag> function for each instruction in
186the packet, and we mark the implicit writes. The analysis determines if the packet
187semantics can be short-circuited. If not, we initialize the result register for each
188of the predicated assignments.
189
190In addition to instruction semantics, we use a generator to create the decode
191tree. This generation is a four step process.
192Step 1 is to run target/hexagon/gen_dectree_import.c to produce
193 <BUILD_DIR>/target/hexagon/iset.py
194Step 2 is to import iset.py into target/hexagon/gen_decodetree.py to produce
195 <BUILD_DIR>/target/hexagon/normal_decode_generated
196 <BUILD_DIR>/target/hexagon/hvx_decode_generated
197 <BUILD_DIR>/target/hexagon/subinsn_*_decode_generated
198Step 3 is to process the above files with QEMU's decodetree.py to produce
199 <BUILD_DIR>/target/hexagon/decode_*_generated.c.inc
200Step 4 is to import iset.py into target/hexagon/gen_trans_funcs.py to produce
201 <BUILD_DIR>/target/hexagon/decodetree_trans_funcs_generated.c.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 example, 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 QRegs Q (vector predicate) registers
281 future_QRegs Registers to be stored during packet commit
282
283*** Debugging ***
284
285You can turn on a lot of debugging by changing the HEX_DEBUG macro to 1 in
286internal.h. This will stream a lot of information as it generates TCG and
287executes the code.
288
289To track down nasty issues with Hexagon->TCG generation, we compare the
290execution results with actual hardware running on a Hexagon Linux target.
291Run qemu with the "-d cpu" option. Then, we can diff the results and figure
292out where qemu and hardware behave differently.
293
294The stacks are located at different locations. We handle this by changing
295env->stack_adjust in translate.c. First, set this to zero and run qemu.
296Then, change env->stack_adjust to the difference between the two stack
297locations. Then rebuild qemu and run again. That will produce a very
298clean diff.
299
300Here are some handy places to set breakpoints
301
302 At the call to gen_start_packet for a given PC (note that the line number
303 might change in the future)
304 br translate.c:602 if ctx->base.pc_next == 0xdeadbeef
305 The helper function for each instruction is named helper_<TAG>, so here's
306 an example that will set a breakpoint at the start
307 br helper_A2_add
308 If you have the HEX_DEBUG macro set, the following will be useful
309 At the start of execution of a packet for a given PC
310 br helper_debug_start_packet if env->gpr[41] == 0xdeadbeef
311 At the end of execution of a packet for a given PC
312 br helper_debug_commit_end if this_PC == 0xdeadbeef
313