get predicated-vectorised branch working
[riscv-isa-sim.git] / riscv / sv.cc
1 #include "sv.h"
2 #include "sv_decode.h"
3
4 sv_pred_entry* sv_insn_t::get_predentry(uint64_t reg, bool intreg)
5 {
6 // okaay so first determine which map to use. intreg is passed
7 // in (ultimately) from id_regs.py's examination of the use of
8 // FRS1/RS1, WRITE_FRD/WRITE_RD, which in turn gets passed
9 // in from sv_insn_t::fimap...
10 sv_pred_entry *r;
11 if (intreg)
12 {
13 return &p->get_state()->sv_pred_int_tb[reg];
14 }
15 else
16 {
17 return &p->get_state()->sv_pred_fp_tb[reg];
18 }
19 }
20
21 sv_reg_entry* sv_insn_t::get_regentry(uint64_t reg, bool intreg)
22 {
23 // okaay so first determine which map to use. intreg is passed
24 // in (ultimately) from id_regs.py's examination of the use of
25 // FRS1/RS1, WRITE_FRD/WRITE_RD, which in turn gets passed
26 // in from sv_insn_t::fimap...
27 sv_reg_entry *r;
28 if (intreg)
29 {
30 return &p->get_state()->sv_int_tb[reg];
31 }
32 else
33 {
34 return &p->get_state()->sv_fp_tb[reg];
35 }
36 }
37
38 bool sv_insn_t::sv_check_reg(bool intreg, uint64_t reg)
39 {
40 sv_reg_entry *r = get_regentry(reg, intreg);
41 if (r->elwidth != 0)
42 {
43 // XXX raise exception
44 }
45 if (r->active && r->isvec)
46 {
47 fprintf(stderr, "checkreg: %ld active\n", reg);
48 at_least_one_reg_vectorised = true;
49 return true;
50 }
51 return false;
52 }
53
54 /* this is the "remap" function. note that registers can STILL BE REDIRECTED
55 * yet NOT BE MARKED AS A VECTOR.
56 *
57 * reg 5 -> active=false, regidx=XX, isvec=XX -> returns 5
58 * reg 5 -> active=true , regidx=35, isvec=false -> returns 35
59 * reg 5 -> active=true , regidx=35, isvec=true -> returns 35 *PLUS LOOP*
60 *
61 * so it is possible for example to use the remap system for C instructions
62 * to get access to the *full* range of registers x0..x63 (yes 63 because
63 * SV doubles both the int and fp regfile sizes), by setting
64 * "active=true, isvec=false" for any of x8..x15
65 *
66 * where "active=true, isvec=true" this is the "expected" behaviour
67 * of SV. it's "supposed" to "just" be a vectorisation API. it isn't:
68 * it's quite a bit more.
69 */
70 uint64_t sv_insn_t::remap(uint64_t reg, bool intreg, int voffs)
71 {
72 // okaay so first determine which map to use. intreg is passed
73 // in (ultimately) from id_regs.py's examination of the use of
74 // FRS1/RS1, WRITE_FRD/WRITE_RD, which in turn gets passed
75 // in from sv_insn_t::fimap...
76 sv_reg_entry *r = get_regentry(reg, intreg);
77
78 // next we check if this entry is active. if not, the register
79 // is not being "redirected", so just return the actual reg.
80 if (!r->active)
81 {
82 return reg; // not active: return as-is
83 }
84 vloop_continue = true;
85
86 // next we go through the lookup table. *THIS* is why the
87 // sv_reg_entry table is 32 entries (5-bit) *NOT* 6 bits
88 // the *KEY* (reg) is 5-bit, the *VALUE* (actual target reg) is 6-bit
89 // XXX TODO: must actually double NXPR and NXFR in processor.h to cope!!
90 reg = r->regidx;
91
92 // now we determine if this is a scalar/vector: if it's scalar
93 // we return the re-mapped register...
94 if (!r->isvec) // scalar
95 {
96 return reg;
97 }
98 vloop_continue = true;
99
100 // aaand now, as it's a "vector", FINALLY we can add on the loop-offset
101 // which was passed in to the sv_insn_t constructor (by reference)
102 // and, at last, we have "parallelism" a la contiguous registers.
103 reg += voffs; // wheww :)
104
105 return reg;
106 }
107
108 /* gets the predication value (if active). returns all-1s if not active
109 * also returns whether zeroing is enabled/disabled for this register.
110 *
111 * uses the same sort of lookup logic as remap:
112 *
113 * - first thing to note is, there is one CSR table for FP and one for INT
114 * (so, FP regs can be predicated separately from INT ones)
115 * - redirection occurs if the CSR entry for the register is "active".
116 * - inversion of the predication can be set (so it's possible to have
117 * the same actual register value be unchanged yet be referred to by
118 * *TWO* redirections, one with inversion, one with not).
119 *
120 * note that this function *actually* returns the value of the (integer)
121 * register file, hence why processor_t has to be passed in
122 *
123 * note also that *even scalar* ops will be predicated (i.e. if a register
124 * has been set active=true and isvec=false in sv_int_tb or sv_fp_tb).
125 * the way to ensure that scalar ops are not predicated is: set VLEN=0,
126 * set active=false in sv_int_tb/sv_fp_tb for that register, or switch off
127 * the predication for that register (sv_pred_int_tb/sv_pred_fb_tb).
128 *
129 * note also that the hard limit on SV maximum vector length is actually
130 * down to the number of bits in the predication i.e. the bitwidth of integer
131 * registers (i.e. XLEN bits).
132 */
133 reg_t sv_insn_t::predicate(uint64_t reg, bool intreg, bool &zeroing)
134 {
135 sv_reg_entry *pr = get_regentry(reg, intreg);
136 if (!pr->active)
137 {
138 return ~0x0; // *REGISTER* not active: return all-1s (unconditional "on")
139 }
140 sv_pred_entry *r = get_predentry(reg, intreg);
141 if (!r->active)
142 {
143 return ~0x0; // *PREDICATION* not active: return all-1s (unconditional "on")
144 }
145 zeroing = r->zero;
146 reg = r->regidx;
147 reg_t predicate = READ_REG(reg); // macros go through processor_t state
148 if (r->inv)
149 {
150 return ~predicate;
151 }
152 return predicate;
153 }
154
155 uint64_t sv_insn_t::predicated(uint64_t reg, int offs, uint64_t pred)
156 {
157 if (pred & (1<<offs))
158 {
159 return reg;
160 }
161 fprintf(stderr, "predication %ld %d %lx\n", reg, offs, pred);
162 return 0;
163 }
164
165 bool sv_insn_t::stop_vloop(void)
166 {
167 return (p->get_state()->vl == 0) || !vloop_continue;
168 }
169
170
171 /* c_lwsp's immediate offset is turned into a Vector "unit stride" if
172 * x2 (sp by convention) is marked as vectorised.
173 *
174 */
175 uint64_t sv_insn_t::_rvc_spoffs_imm(uint64_t elwidth, uint64_t offs)
176 {
177 sv_reg_entry *r = get_regentry(X_SP, 1);
178 if (!r->active)
179 {
180 return offs;
181 }
182 vloop_continue = true;
183 reg_t reg = r->regidx;
184 if (!r->isvec)
185 {
186 return offs;
187 }
188 offs += (*offs_imm) * elwidth;
189
190 return offs;
191 }
192
193 // for use in predicated branches. sets bit N if val=true; clears bit N if false
194 uint64_t sv_insn_t::rd_bitset(reg_t reg, uint64_t bit, bool set)
195 {
196 uint64_t val = READ_REG(reg);
197 if (set) {
198 val |= (1<<bit);
199 } else {
200 val &= ~(1<<bit);
201 }
202 WRITE_REG(reg, val);
203 return val;
204 }
205
206 void sv_insn_t::setpc(int xlen, int vlen, reg_t &npc, reg_t addr, uint64_t offs,
207 reg_t *target_reg)
208 {
209 save_branch_addr = addr;
210 if (not at_least_one_reg_vectorised)
211 {
212 _set_pc(addr);
213 return;
214 }
215 if (target_reg != NULL) {
216 fprintf(stderr, "setpc pre rd %ld v %lx pred %lx\n",
217 *target_reg, READ_REG(*target_reg), prs1);
218 }
219 if ((1<<offs) & prs1)
220 {
221 if (target_reg) {
222 save_branch_rd = rd_bitset(*target_reg, offs, true);
223 } else {
224 save_branch_rd |= (1<<offs);
225 }
226 }
227 fprintf(stderr, "setpc %lx offs %ld predicate %lx rs1 %ld rs2 %ld\n",
228 save_branch_rd, offs, prs1,
229 READ_REG(rs1()), READ_REG(rs2()));
230 }
231