1 /*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <cstdio>
8 #include <iterator>
9 #include "pio_assembler.h"
10 #include "parser.hpp"
11
12 #ifdef _MSC_VER
13 #pragma warning(disable : 4996) // fopen
14 #endif
15
16 using syntax_error = yy::parser::syntax_error;
17
18 std::string output_format::default_name = "c-sdk";
19
pio_assembler()20 pio_assembler::pio_assembler() {
21 }
22
generate(std::shared_ptr<output_format> _format,const std::string & _source,const std::string & _dest,const std::vector<std::string> & _options)23 int pio_assembler::generate(std::shared_ptr<output_format> _format, const std::string &_source,
24 const std::string &_dest, const std::vector<std::string> &_options) {
25 format = _format;
26 source = _source;
27 dest = _dest;
28 options = _options;
29 location.initialize(&source);
30 scan_begin();
31 yy::parser parse(*this);
32 // parse.set_debug_level(false);
33 int res = parse();
34 scan_end();
35 return res;
36 }
37
add_instruction(std::shared_ptr<instruction> inst)38 void program::add_instruction(std::shared_ptr<instruction> inst) {
39 uint limit = MAX_INSTRUCTIONS;
40 if (instructions.size() >= limit) {
41 // todo take offset into account
42 std::stringstream msg;
43 msg << "program instruction limit of " << limit << " instruction(s) exceeded";
44 throw syntax_error(inst->location, msg.str());
45 }
46 if (!sideset_opt && !inst->sideset) {
47 std::stringstream msg;
48 msg << "instruction requires 'side' to specify side set value for the instruction because non optional sideset was specified for the program at " << sideset.location;
49 throw syntax_error(inst->location, msg.str());
50 }
51 instructions.push_back(inst);
52 }
53
54 using syntax_error = yy::parser::syntax_error;
55
add_symbol(std::shared_ptr<symbol> symbol)56 void program::add_symbol(std::shared_ptr<symbol> symbol) {
57 const auto &existing = pioasm->get_symbol(symbol->name, this);
58 if (existing) {
59 std::stringstream msg;
60 if (symbol->is_label != existing->is_label) {
61 msg << "'" << symbol->name << "' was already defined as a " << (existing->is_label ? "label" : "value")
62 << " at " << existing->location;
63 } else if (symbol->is_label) {
64 msg << "label '" << symbol->name << "' was already defined at " << existing->location;
65 } else {
66 msg << "'" << symbol->name << "' was already defined at " << existing->location;
67 }
68 throw syntax_error(symbol->location, msg.str());
69 }
70 symbols.insert(std::pair<std::string, std::shared_ptr<::symbol>>(symbol->name, symbol));
71 ordered_symbols.push_back(symbol);
72 }
73
resolve(const program & program)74 int resolvable::resolve(const program &program) {
75 return resolve(program.pioasm, &program);
76 }
77
resolve(pio_assembler * pioasm,const program * program,const resolvable & scope)78 int unary_operation::resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) {
79 int value = arg->resolve(pioasm, program, scope);
80 switch (op) {
81 case negate:
82 return -value;
83 case reverse: {
84 // slow is fine
85 uint result = 0;
86 for (uint i = 0; i < 32; i++) {
87 result <<= 1u;
88 if (value & 1u) {
89 result |= 1u;
90 }
91 value >>= 1u;
92 }
93 return result;
94 }
95 default:
96 throw syntax_error(location, "internal error");
97 }
98 }
99
resolve(pio_assembler * pioasm,const program * program,const resolvable & scope)100 int binary_operation::resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) {
101 int lvalue = left->resolve(pioasm, program, scope);
102 int rvalue = right->resolve(pioasm, program, scope);
103 switch (op) {
104 case add:
105 return lvalue + rvalue;
106 case subtract:
107 return lvalue - rvalue;
108 case multiply:
109 return lvalue * rvalue;
110 case divide:
111 return lvalue / rvalue;
112 case and_:
113 return lvalue & rvalue;
114 case or_:
115 return lvalue | rvalue;
116 case xor_:
117 return lvalue ^ rvalue;
118 default:
119 throw syntax_error(location, "internal error");
120 }
121 }
122
set_wrap(const yy::location & l)123 void program::set_wrap(const yy::location &l) {
124 if (wrap) {
125 std::stringstream msg;
126 msg << ".wrap was already specified at " << wrap->location;
127 throw syntax_error(l, msg.str());
128 }
129 if (instructions.empty()) {
130 throw syntax_error(l, ".wrap cannot be placed before the first program instruction");
131 }
132 wrap = resolvable_int(l, instructions.size() - 1);
133 }
134
set_wrap_target(const yy::location & l)135 void program::set_wrap_target(const yy::location &l) {
136 if (wrap_target) {
137 std::stringstream msg;
138 msg << ".wrap_target was already specified at " << wrap_target->location;
139 throw syntax_error(l, msg.str());
140 }
141 wrap_target = resolvable_int(l, instructions.size());
142 }
143
add_code_block(const code_block & block)144 void program::add_code_block(const code_block &block) {
145 code_blocks[block.lang].push_back(block);
146 }
147
add_lang_opt(std::string lang,std::string name,std::string value)148 void program::add_lang_opt(std::string lang, std::string name, std::string value) {
149 lang_opts[lang].emplace_back(name, value);
150 }
151
finalize()152 void program::finalize() {
153 if (sideset.value) {
154 int bits = sideset.value->resolve(*this);
155 if (bits < 0) {
156 throw syntax_error(sideset.value->location, "number of side set bits must be positive");
157 }
158 sideset_max = (1u << bits) - 1;
159 if (sideset_opt) bits++;
160 sideset_bits_including_opt = bits;
161 if (bits > 5) {
162 if (sideset_opt)
163 throw syntax_error(sideset.value->location, "maximum number of side set bits with optional is 4");
164 else
165 throw syntax_error(sideset.value->location, "maximum number of side set bits is 5");
166 }
167 delay_max = (1u << (5 - bits)) - 1;
168 } else {
169 sideset_max = 0;
170 delay_max = 31;
171 }
172 }
173
resolve(pio_assembler * pioasm,const program * program,const resolvable & scope)174 int name_ref::resolve(pio_assembler *pioasm, const program *program, const resolvable &scope) {
175 auto symbol = pioasm->get_symbol(name, program);
176 if (symbol) {
177 if (symbol->resolve_started) {
178 std::stringstream msg;
179 msg << "circular dependency in definition of '" << name << "'; detected at " << location << ")";
180 throw syntax_error(scope.location, msg.str());
181 }
182 try {
183 symbol->resolve_started++;
184 int rc = symbol->value->resolve(pioasm, program, scope);
185 symbol->resolve_started--;
186 return rc;
187 } catch (syntax_error &e) {
188 symbol->resolve_started--;
189 throw e;
190 }
191 } else {
192 std::stringstream msg;
193 msg << "undefined symbol '" << name << "'";
194 throw syntax_error(location, msg.str());
195 }
196 }
197
encode(const program & program)198 uint instruction::encode(const program &program) {
199 raw_encoding raw = raw_encode(program);
200 int _delay = delay->resolve(program);
201 if (_delay < 0) {
202 throw syntax_error(delay->location, "instruction delay must be positive");
203 }
204 if (_delay > program.delay_max) {
205 if (program.delay_max == 31) {
206 throw syntax_error(delay->location, "instruction delay must be <= 31");
207 } else {
208 std::stringstream msg;
209 msg << "the instruction delay limit is " << program.delay_max << " because of the side set specified at "
210 << program.sideset.location;
211 throw syntax_error(delay->location, msg.str());
212 }
213 }
214 int _sideset = 0;
215 if (sideset) {
216 _sideset = sideset->resolve(program);
217 if (_sideset < 0) {
218 throw syntax_error(sideset->location, "side set value must be >=0");
219 }
220 if (_sideset > program.sideset_max) {
221 std::stringstream msg;
222 msg << "the maximum side set value is " << program.sideset_max << " based on the configuration specified at "
223 << program.sideset.location;
224 throw syntax_error(sideset->location, msg.str());
225 }
226 _sideset <<= (5u - program.sideset_bits_including_opt);
227 if (program.sideset_opt) {
228 _sideset |= 0x10u;
229 }
230 }
231 return (((uint) raw.type) << 13u) | (((uint) _delay | (uint) _sideset) << 8u) | (raw.arg1 << 5u) | raw.arg2;
232 }
233
raw_encode(const program & program)234 raw_encoding instruction::raw_encode(const program &program) {
235 throw syntax_error(location, "internal error");
236 }
237
encode(const program & program)238 uint instr_word::encode(const program &program) {
239 uint value = encoding->resolve(program);
240 if (value > 0xffffu) {
241 throw syntax_error(location, ".word value must be a positive 16 bit value");
242 }
243 return value;
244 }
245
raw_encode(const program & program)246 raw_encoding instr_jmp::raw_encode(const program &program) {
247 int dest = target->resolve(program);
248 if (dest < 0) {
249 throw syntax_error(target->location, "jmp target address must be positive");
250 } else if (dest >= (int)program.instructions.size()) {
251 std::stringstream msg;
252 msg << "jmp target address " << dest << " is beyond the end of the program";
253 throw syntax_error(target->location, msg.str());
254 }
255 return {inst_type::jmp, (uint) cond, (uint) dest};
256 }
257
raw_encode(const program & program)258 raw_encoding instr_in::raw_encode(const program &program) {
259 int v = value->resolve(program);
260 if (v < 1 || v > 32) {
261 throw syntax_error(value->location, "'in' bit count must be >= 1 and <= 32");
262 }
263 return {inst_type::in, (uint) src, (uint) v & 0x1fu};
264 }
265
raw_encode(const program & program)266 raw_encoding instr_out::raw_encode(const program &program) {
267 int v = value->resolve(program);
268 if (v < 1 || v > 32) {
269 throw syntax_error(value->location, "'out' bit count must be >= 1 and <= 32");
270 }
271 return {inst_type::out, (uint) dest, (uint) v & 0x1fu};
272 }
273
raw_encode(const program & program)274 raw_encoding instr_set::raw_encode(const program &program) {
275 int v = value->resolve(program);
276 if (v < 0 || v > 31) {
277 throw syntax_error(value->location, "'set' bit count must be >= 0 and <= 31");
278 }
279 return {inst_type::set, (uint) dest, (uint) v};
280 }
281
raw_encode(const program & program)282 raw_encoding instr_wait::raw_encode(const program &program) {
283 uint pol = polarity->resolve(program);
284 if (pol > 1) {
285 throw syntax_error(polarity->location, "'wait' polarity must be 0 or 1");
286 }
287 uint arg2 = source->param->resolve(program);
288 switch (source->target) {
289 case wait_source::irq:
290 if (arg2 > 7) throw syntax_error(source->param->location, "irq number must be must be >= 0 and <= 7");
291 break;
292 case wait_source::gpio:
293 if (arg2 > 31)
294 throw syntax_error(source->param->location, "absolute GPIO number must be must be >= 0 and <= 31");
295 break;
296 case wait_source::pin:
297 if (arg2 > 31) throw syntax_error(polarity->location, "pin number must be must be >= 0 and <= 31");
298 break;
299 }
300 return {inst_type::wait, (pol << 2u) | (uint) source->target, arg2 | (source->flag ? 0x10u : 0u)};
301 }
302
raw_encode(const program & program)303 raw_encoding instr_irq::raw_encode(const program &program) {
304 uint arg2 = num->resolve(program);
305 if (arg2 > 7) throw syntax_error(num->location, "irq number must be must be >= 0 and <= 7");
306 if (relative) arg2 |= 0x10u;
307 return {inst_type::irq, (uint)modifiers, arg2};
308 }
309
public_symbols(program & program)310 std::vector<compiled_source::symbol> pio_assembler::public_symbols(program &program) {
311 std::vector<std::shared_ptr<symbol>> public_symbols;
312 std::remove_copy_if(program.ordered_symbols.begin(), program.ordered_symbols.end(),
313 std::inserter(public_symbols, public_symbols.end()),
314 [](const std::shared_ptr<symbol> &s) { return !s->is_public; });
315
316 std::vector<compiled_source::symbol> rc;
317 std::transform(public_symbols.begin(), public_symbols.end(), std::back_inserter(rc),
318 [&](const std::shared_ptr<symbol> &s) {
319 return compiled_source::symbol(s->name, s->value->resolve(program), s->is_label);
320 });
321 return rc;
322 }
323
write_output()324 int pio_assembler::write_output() {
325 std::set<std::string> known_output_formats;
326 std::transform(output_format::all().begin(), output_format::all().end(),
327 std::inserter(known_output_formats, known_output_formats.begin()),
328 [&](std::shared_ptr<output_format> &f) {
329 return f->name;
330 });
331
332 compiled_source source;
333 source.global_symbols = public_symbols(get_dummy_global_program());
334 for (auto &program : programs) {
335 program.finalize();
336 source.programs.emplace_back(compiled_source::program(program.name));
337 auto &cprogram = source.programs[source.programs.size() - 1];
338 cprogram = compiled_source::program(program.name);
339
340 // encode the instructions
341 std::transform(program.instructions.begin(), program.instructions.end(),
342 std::back_inserter(cprogram.instructions), [&](std::shared_ptr<instruction> &inst) {
343 return inst->encode(program);
344 });
345
346 for (const auto &e : program.code_blocks) {
347 bool ok = false;
348 for(const auto &o : known_output_formats) {
349 if (o == e.first || 0 == e.first.find(o+"-")) {
350 ok = true;
351 break;
352 }
353 }
354 if (!ok) {
355 std::cerr << e.second[0].location << ": warning, unknown code block output type '" << e.first << "'\n";
356 known_output_formats.insert(e.first);
357 }
358 }
359
360 if (program.wrap) cprogram.wrap = program.wrap->resolve(program); else cprogram.wrap = std::max((int)program.instructions.size() - 1, 0);
361 if (program.wrap_target) cprogram.wrap_target = program.wrap_target->resolve(program); else cprogram.wrap_target = 0;
362 if (program.origin.value) cprogram.origin = program.origin.value->resolve(program);
363 if (program.sideset.value) {
364 cprogram.sideset_bits_including_opt = program.sideset_bits_including_opt;
365 cprogram.sideset_opt = program.sideset_opt;
366 cprogram.sideset_pindirs = program.sideset_pindirs;
367 }
368 std::transform(program.code_blocks.begin(), program.code_blocks.end(), std::inserter(cprogram.code_blocks, cprogram.code_blocks.begin()), [](const std::pair<std::string, std::vector<code_block>>&e) {
369 std::vector<std::string> blocks;
370 std::transform(e.second.begin(), e.second.end(), std::back_inserter(blocks), [&](const code_block& block) {
371 return block.contents;
372 });
373 return std::pair<std::string, std::vector<std::string>>(e.first, blocks);
374 });
375 cprogram.lang_opts = program.lang_opts;
376 cprogram.symbols = public_symbols(program);
377 }
378 if (programs.empty()) {
379 std::cout << "warning: input contained no programs" << std::endl;
380 }
381 return format->output(dest, options, source);
382 }
383
open_single_output(std::string destination)384 FILE *output_format::open_single_output(std::string destination) {
385 FILE *out = destination == "-" ? stdout : fopen(destination.c_str(), "w");
386 if (!out) {
387 std::cerr << "Can't open output file '" << destination << "'" << std::endl;
388 }
389 return out;
390 }
391