2021-01-20 16:44:27 +00:00
/*
* Copyright ( c ) 2020 Raspberry Pi ( Trading ) Ltd .
*
* SPDX - License - Identifier : BSD - 3 - Clause
*/
# include <cstdio>
# include <iterator>
# include "pio_assembler.h"
# include "parser.hpp"
# ifdef _MSC_VER
# pragma warning(disable : 4996) // fopen
# endif
using syntax_error = yy : : parser : : syntax_error ;
std : : string output_format : : default_name = " c-sdk " ;
pio_assembler : : pio_assembler ( ) {
}
int pio_assembler : : generate ( std : : shared_ptr < output_format > _format , const std : : string & _source ,
const std : : string & _dest , const std : : vector < std : : string > & _options ) {
format = _format ;
source = _source ;
dest = _dest ;
options = _options ;
location . initialize ( & source ) ;
scan_begin ( ) ;
yy : : parser parse ( * this ) ;
// parse.set_debug_level(false);
int res = parse ( ) ;
scan_end ( ) ;
return res ;
}
void program : : add_instruction ( std : : shared_ptr < instruction > inst ) {
uint limit = MAX_INSTRUCTIONS ;
if ( instructions . size ( ) > = limit ) {
// todo take offset into account
std : : stringstream msg ;
msg < < " program instruction limit of " < < limit < < " instruction(s) exceeded " ;
throw syntax_error ( inst - > location , msg . str ( ) ) ;
}
if ( ! sideset_opt & & ! inst - > sideset ) {
std : : stringstream msg ;
msg < < " instruction requires 'side' to specify side set value for the instruction because non optional sideset was specified for the program at " < < sideset . location ;
throw syntax_error ( inst - > location , msg . str ( ) ) ;
}
instructions . push_back ( inst ) ;
}
using syntax_error = yy : : parser : : syntax_error ;
void program : : add_symbol ( std : : shared_ptr < symbol > symbol ) {
const auto & existing = pioasm - > get_symbol ( symbol - > name , this ) ;
if ( existing ) {
std : : stringstream msg ;
if ( symbol - > is_label ! = existing - > is_label ) {
msg < < " ' " < < symbol - > name < < " ' was already defined as a " < < ( existing - > is_label ? " label " : " value " )
< < " at " < < existing - > location ;
} else if ( symbol - > is_label ) {
msg < < " label ' " < < symbol - > name < < " ' was already defined at " < < existing - > location ;
} else {
msg < < " ' " < < symbol - > name < < " ' was already defined at " < < existing - > location ;
}
throw syntax_error ( symbol - > location , msg . str ( ) ) ;
}
symbols . insert ( std : : pair < std : : string , std : : shared_ptr < : : symbol > > ( symbol - > name , symbol ) ) ;
ordered_symbols . push_back ( symbol ) ;
}
int resolvable : : resolve ( const program & program ) {
return resolve ( program . pioasm , & program ) ;
}
int unary_operation : : resolve ( pio_assembler * pioasm , const program * program , const resolvable & scope ) {
int value = arg - > resolve ( pioasm , program , scope ) ;
switch ( op ) {
case negate :
return - value ;
case reverse : {
// slow is fine
uint result = 0 ;
for ( uint i = 0 ; i < 32 ; i + + ) {
result < < = 1u ;
if ( value & 1u ) {
result | = 1u ;
}
value > > = 1u ;
}
return result ;
}
default :
throw syntax_error ( location , " internal error " ) ;
}
}
int binary_operation : : resolve ( pio_assembler * pioasm , const program * program , const resolvable & scope ) {
int lvalue = left - > resolve ( pioasm , program , scope ) ;
int rvalue = right - > resolve ( pioasm , program , scope ) ;
switch ( op ) {
case add :
return lvalue + rvalue ;
case subtract :
return lvalue - rvalue ;
case multiply :
return lvalue * rvalue ;
case divide :
return lvalue / rvalue ;
case and_ :
return lvalue & rvalue ;
case or_ :
return lvalue | rvalue ;
case xor_ :
return lvalue ^ rvalue ;
default :
throw syntax_error ( location , " internal error " ) ;
}
}
void program : : set_wrap ( const yy : : location & l ) {
if ( wrap ) {
std : : stringstream msg ;
msg < < " .wrap was already specified at " < < wrap - > location ;
throw syntax_error ( l , msg . str ( ) ) ;
}
if ( instructions . empty ( ) ) {
throw syntax_error ( l , " .wrap cannot be pleaced before the first program instruction " ) ;
}
wrap = resolvable_int ( l , instructions . size ( ) - 1 ) ;
}
void program : : set_wrap_target ( const yy : : location & l ) {
if ( wrap_target ) {
std : : stringstream msg ;
msg < < " .wrap_target was already specified at " < < wrap_target - > location ;
throw syntax_error ( l , msg . str ( ) ) ;
}
wrap_target = resolvable_int ( l , instructions . size ( ) ) ;
}
void program : : add_code_block ( const code_block & block ) {
code_blocks [ block . lang ] . push_back ( block ) ;
}
void program : : add_lang_opt ( std : : string lang , std : : string name , std : : string value ) {
lang_opts [ lang ] . emplace_back ( name , value ) ;
}
void program : : finalize ( ) {
if ( sideset . value ) {
int bits = sideset . value - > resolve ( * this ) ;
if ( bits < 0 ) {
throw syntax_error ( sideset . value - > location , " number of side set bits must be positive " ) ;
}
sideset_max = ( 1u < < bits ) - 1 ;
if ( sideset_opt ) bits + + ;
sideset_bits_including_opt = bits ;
if ( bits > 5 ) {
if ( sideset_opt )
throw syntax_error ( sideset . value - > location , " maximum number of side set bits with optional is 4 " ) ;
else
throw syntax_error ( sideset . value - > location , " maximum number of side set bits is 5 " ) ;
}
delay_max = ( 1u < < ( 5 - bits ) ) - 1 ;
} else {
sideset_max = 0 ;
delay_max = 31 ;
}
}
int name_ref : : resolve ( pio_assembler * pioasm , const program * program , const resolvable & scope ) {
auto symbol = pioasm - > get_symbol ( name , program ) ;
if ( symbol ) {
if ( symbol - > resolve_started ) {
std : : stringstream msg ;
msg < < " circular dependency in definition of ' " < < name < < " '; detected at " < < location < < " ) " ;
throw syntax_error ( scope . location , msg . str ( ) ) ;
}
try {
symbol - > resolve_started + + ;
int rc = symbol - > value - > resolve ( pioasm , program , scope ) ;
symbol - > resolve_started - - ;
return rc ;
} catch ( syntax_error & e ) {
symbol - > resolve_started - - ;
throw e ;
}
} else {
std : : stringstream msg ;
msg < < " undefined symbol ' " < < name < < " ' " ;
throw syntax_error ( location , msg . str ( ) ) ;
}
}
uint instruction : : encode ( const program & program ) {
raw_encoding raw = raw_encode ( program ) ;
int _delay = delay - > resolve ( program ) ;
if ( _delay < 0 ) {
throw syntax_error ( delay - > location , " instruction delay must be positive " ) ;
}
if ( _delay > program . delay_max ) {
if ( program . delay_max = = 31 ) {
throw syntax_error ( delay - > location , " instruction delay must be <= 31 " ) ;
} else {
std : : stringstream msg ;
msg < < " the instruction delay limit is " < < program . delay_max < < " because of the side set specified at "
< < program . sideset . location ;
throw syntax_error ( delay - > location , msg . str ( ) ) ;
}
}
int _sideset = 0 ;
if ( sideset ) {
_sideset = sideset - > resolve ( program ) ;
if ( _sideset < 0 ) {
throw syntax_error ( sideset - > location , " side set value must be >=0 " ) ;
}
if ( _sideset > program . sideset_max ) {
std : : stringstream msg ;
msg < < " the maximum side set value is " < < program . sideset_max < < " based on the configuration specified at "
< < program . sideset . location ;
throw syntax_error ( sideset - > location , msg . str ( ) ) ;
}
_sideset < < = ( 5u - program . sideset_bits_including_opt ) ;
if ( program . sideset_opt ) {
_sideset | = 0x10u ;
}
}
return ( ( ( uint ) raw . type ) < < 13u ) | ( ( ( uint ) _delay | ( uint ) _sideset ) < < 8u ) | ( raw . arg1 < < 5u ) | raw . arg2 ;
}
raw_encoding instruction : : raw_encode ( const program & program ) {
throw syntax_error ( location , " internal error " ) ;
}
uint instr_word : : encode ( const program & program ) {
uint value = encoding - > resolve ( program ) ;
if ( value > 0xffffu ) {
throw syntax_error ( location , " .word value must be a positive 16 bit value " ) ;
}
return value ;
}
raw_encoding instr_jmp : : raw_encode ( const program & program ) {
int dest = target - > resolve ( program ) ;
if ( dest < 0 ) {
throw syntax_error ( target - > location , " jmp target address must be positive " ) ;
} else if ( dest > = ( int ) program . instructions . size ( ) ) {
std : : stringstream msg ;
msg < < " jmp target address " < < dest < < " is beyond the end of the program " ;
throw syntax_error ( target - > location , msg . str ( ) ) ;
}
return { inst_type : : jmp , ( uint ) cond , ( uint ) dest } ;
}
raw_encoding instr_in : : raw_encode ( const program & program ) {
int v = value - > resolve ( program ) ;
if ( v < 1 | | v > 32 ) {
throw syntax_error ( value - > location , " 'in' bit count must be >= 1 and <= 32 " ) ;
}
return { inst_type : : in , ( uint ) src , ( uint ) v & 0x1fu } ;
}
raw_encoding instr_out : : raw_encode ( const program & program ) {
int v = value - > resolve ( program ) ;
if ( v < 1 | | v > 32 ) {
throw syntax_error ( value - > location , " 'out' bit count must be >= 1 and <= 32 " ) ;
}
return { inst_type : : out , ( uint ) dest , ( uint ) v & 0x1fu } ;
}
raw_encoding instr_set : : raw_encode ( const program & program ) {
int v = value - > resolve ( program ) ;
if ( v < 0 | | v > 31 ) {
throw syntax_error ( value - > location , " 'set' bit count must be >= 0 and <= 31 " ) ;
}
return { inst_type : : set , ( uint ) dest , ( uint ) v } ;
}
raw_encoding instr_wait : : raw_encode ( const program & program ) {
uint pol = polarity - > resolve ( program ) ;
if ( pol > 1 ) {
throw syntax_error ( polarity - > location , " 'wait' polarity must be 0 or 1 " ) ;
}
uint arg2 = source - > param - > resolve ( program ) ;
switch ( source - > target ) {
case wait_source : : irq :
if ( arg2 > 7 ) throw syntax_error ( source - > param - > location , " irq number must be must be >= 0 and <= 7 " ) ;
break ;
case wait_source : : gpio :
if ( arg2 > 31 )
throw syntax_error ( source - > param - > location , " absolute GPIO number must be must be >= 0 and <= 31 " ) ;
break ;
case wait_source : : pin :
if ( arg2 > 31 ) throw syntax_error ( polarity - > location , " pin number must be must be >= 0 and <= 31 " ) ;
break ;
}
return { inst_type : : wait , ( pol < < 2u ) | ( uint ) source - > target , arg2 | ( source - > flag ? 0x10u : 0u ) } ;
}
raw_encoding instr_irq : : raw_encode ( const program & program ) {
uint arg2 = num - > resolve ( program ) ;
if ( arg2 > 7 ) throw syntax_error ( num - > location , " irq number must be must be >= 0 and <= 7 " ) ;
2021-01-25 11:10:32 +00:00
if ( relative ) arg2 | = 0x10u ;
2021-01-20 16:44:27 +00:00
return { inst_type : : irq , ( uint ) modifiers , arg2 } ;
}
std : : vector < compiled_source : : symbol > pio_assembler : : public_symbols ( program & program ) {
std : : vector < std : : shared_ptr < symbol > > public_symbols ;
std : : remove_copy_if ( program . ordered_symbols . begin ( ) , program . ordered_symbols . end ( ) ,
std : : inserter ( public_symbols , public_symbols . end ( ) ) ,
[ ] ( const std : : shared_ptr < symbol > & s ) { return ! s - > is_public ; } ) ;
std : : vector < compiled_source : : symbol > rc ;
std : : transform ( public_symbols . begin ( ) , public_symbols . end ( ) , std : : back_inserter ( rc ) ,
[ & ] ( const std : : shared_ptr < symbol > & s ) {
return compiled_source : : symbol ( s - > name , s - > value - > resolve ( program ) , s - > is_label ) ;
} ) ;
return rc ;
}
int pio_assembler : : write_output ( ) {
std : : set < std : : string > known_output_formats ;
2021-02-01 16:04:34 +00:00
std : : transform ( output_format : : all ( ) . begin ( ) , output_format : : all ( ) . end ( ) ,
2021-01-20 16:44:27 +00:00
std : : inserter ( known_output_formats , known_output_formats . begin ( ) ) ,
[ & ] ( std : : shared_ptr < output_format > & f ) {
return f - > name ;
} ) ;
compiled_source source ;
source . global_symbols = public_symbols ( get_dummy_global_program ( ) ) ;
for ( auto & program : programs ) {
program . finalize ( ) ;
source . programs . emplace_back ( compiled_source : : program ( program . name ) ) ;
auto & cprogram = source . programs [ source . programs . size ( ) - 1 ] ;
cprogram = compiled_source : : program ( program . name ) ;
// encode the instructions
std : : transform ( program . instructions . begin ( ) , program . instructions . end ( ) ,
std : : back_inserter ( cprogram . instructions ) , [ & ] ( std : : shared_ptr < instruction > & inst ) {
return inst - > encode ( program ) ;
} ) ;
for ( const auto & e : program . code_blocks ) {
bool ok = false ;
for ( const auto & o : known_output_formats ) {
if ( o = = e . first | | 0 = = e . first . find ( o + " - " ) ) {
ok = true ;
break ;
}
}
if ( ! ok ) {
std : : cerr < < e . second [ 0 ] . location < < " : warning, unknown code block output type ' " < < e . first < < " ' \n " ;
known_output_formats . insert ( e . first ) ;
}
}
if ( program . wrap ) cprogram . wrap = program . wrap - > resolve ( program ) ; else cprogram . wrap = std : : max ( ( int ) program . instructions . size ( ) - 1 , 0 ) ;
if ( program . wrap_target ) cprogram . wrap_target = program . wrap_target - > resolve ( program ) ; else cprogram . wrap_target = 0 ;
if ( program . origin . value ) cprogram . origin = program . origin . value - > resolve ( program ) ;
if ( program . sideset . value ) {
cprogram . sideset_bits_including_opt = program . sideset_bits_including_opt ;
cprogram . sideset_opt = program . sideset_opt ;
cprogram . sideset_pindirs = program . sideset_pindirs ;
}
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 ) {
std : : vector < std : : string > blocks ;
std : : transform ( e . second . begin ( ) , e . second . end ( ) , std : : back_inserter ( blocks ) , [ & ] ( const code_block & block ) {
return block . contents ;
} ) ;
return std : : pair < std : : string , std : : vector < std : : string > > ( e . first , blocks ) ;
} ) ;
cprogram . lang_opts = program . lang_opts ;
cprogram . symbols = public_symbols ( program ) ;
}
if ( programs . empty ( ) ) {
std : : cout < < " warning: input contained no programs " < < std : : endl ;
}
return format - > output ( dest , options , source ) ;
}
FILE * output_format : : open_single_output ( std : : string destination ) {
FILE * out = destination = = " - " ? stdout : fopen ( destination . c_str ( ) , " w " ) ;
if ( ! out ) {
std : : cerr < < " Can't open output file ' " < < destination < < " ' " < < std : : endl ;
}
return out ;
}