2012-07-01 01:07:59 +03:00
-- Generate a C configuration starting from a Lua description file
-- for an eLua board
module ( ... , package.seeall )
package.path = package.path .. " ;utils/?.lua;config/?.lua "
local comps = require " components "
local cfgs = require " configurations "
local gen = require " generators "
local utils = require " utils "
local bd = require " build_data "
local mgen = require " modules "
local cpuct = require " cpuconstants "
local sects = require " sections "
local components , configs
2012-07-01 20:47:33 +03:00
local glconf , glen
2012-07-01 01:07:59 +03:00
-------------------------------------------------------------------------------
-- Various helpers and internal functions
-- Generator for section 'components'
local function generate_components ( data , plconf )
local compdata = data.components or { }
-- Prerequisites: check for keys that might be needed in components, but might not be there
-- At the moment, we need to definer either BUILD_CON_GENERIC or BUILD_CON_TCP (but not both)
if compdata.sercon and compdata.tcpipcon then
return nil , " serial and TCP/IP console can't be enabled at the same time in section 'components' "
elseif not compdata.sercon and not compdata.tcpipcon then
2012-07-01 12:19:19 +03:00
return nil , " either serial (sercon) or TCP/IP (tcpipcon) console must be enabled in 'components' "
2012-07-01 01:07:59 +03:00
end
-- Configure section first
local res , err = sects.configure_section ( components , ' components ' , compdata )
if not res then return false , err end
-- Let the backend do its validation
if plconf.pre_generate_section then
res , err = plconf.pre_generate_section ( components , ' components ' , compdata , conf , enabled )
if not res then return false , err end
end
-- Generate all data for section 'components'
return sects.generate_section ( components , ' components ' , compdata )
end
-- Generator for section 'config'
local function generate_config ( data , plconf )
local confdata = data.config or { }
-- Configure section first
local res , err = sects.configure_section ( configs , ' config ' , confdata )
if not res then return false , err end
-- Let the backend do its validation
if plconf.pre_generate_section then
res , err = plconf.pre_generate_section ( configs , ' config ' , confdata , conf , enabled )
if not res then return false , err end
end
return sects.generate_section ( configs , ' config ' , confdata )
end
2012-07-01 20:47:33 +03:00
-- Global sanity checks (data is in 'glconf' and 'glen'
local function check_components_and_config ( )
-- Check all uart IDs. If VUARTs are specified but sermux is not enabled, return with error
if not glen.sermux then
for _ , attrdata in pairs ( glconf ) do
local attrval = attrdata.value
if attrdata.desc . is_uart and type ( attrval ) == " string " and attrval : find ( " ^vuart " ) then
return false , sf ( " attribute '%s' of element '%s' in section '%s' reffers to a virtual UART, but the serial multiplexer ('sermux') is not enabled " ,
attrdata.name , attrdata.elname , attrdata.sectname )
end
end
end
-- Check all timer IDs. If virtual timers are specified but vtmr is not enabled, return with error
if not glen.vtmr or tostring ( glconf.VTMR_NUM_TIMERS . value ) == " 0 " then
for _ , attrdata in pairs ( glconf ) do
local attrval = attrdata.value
if attrdata.desc . is_timer and type ( attrval ) == " string " and attrval : find ( " ^vtmr " ) then
return false , sf ( " attribute '%s' of element '%s' in section '%s' reffers to a virtual timer, but virtual timers ('vtmr') are not enabled " ,
attrdata.name , attrdata.elname , attrdata.sectname )
end
end
end
-- Check sermux/RFS+console proper UART assignment
if glen.sermux and glen.sercon and glen.rfs then
local rfs_uart_value = tostring ( glconf.RFS_UART_ID . value )
local con_uart_value = tostring ( glconf.CON_UART_ID . value )
if rfs_uart_value : find ( " ^vuart " ) and rfs_uart_value ~= " vuart0 " then
io.write ( utils.col_yellow ( " [CONFIG] WARNING: you have enabled the serial multiplexer and RFS over a virtual serial port which is not the first virtual serial port ('vuart0') " ) )
print ( utils.col_yellow ( " In this configuration, the serial multiplexer will not work in 'rfsmux' mode. Check the serial multiplexer section of the eLua manual for more details. " ) )
elseif rfs_uart_value == " vuart0 " and con_uart_value : find ( " ^vuart " ) and con_uart_value ~= " vuart1 " then
io.write ( utils.col_yellow ( " [CONFIG] WARNING: when using both RFS and the serial console with 'sermux', it's best to set the serial console uart ID to the second virtual serial port ('vuart1') " ) )
print ( utils.col_yellow ( " In this configuration, the serial multiplexer will work directly in 'rfsmux' mode with a console. Check the serial multiplexer section of the eLua manual for more details. " ) )
end
end
return true
end
2012-07-01 01:07:59 +03:00
-- Default table for backend configuration data
-- If the platform doesn't have a boardconf.lua, this is what we're going to use
local default_platform_conf = {
add_platform_components = function ( ) end ,
add_platform_configs = function ( ) end ,
get_platform_modules = function ( ) end ,
pre_generate_section = function ( ) return true end ,
}
-- Sanity code
-- These are more checks added to the generated header file
-- Some of these are the result of pure paranoia. Nevertheless, they seem to work.
local sanity_code = [ [
///////////////////////////////////////////////////////////////////////////////
// Static sanity checks and additional defines
# if defined ( ELUA_BOOT_RPC ) && ! defined ( BUILD_RPC )
# define BUILD_RPC
# endif
# if defined ( BUILD_LUA_INT_HANDLERS ) || defined ( BUILD_C_INT_HANDLERS )
# define BUILD_INT_HANDLERS
# ifndef INT_TMR_MATCH
# define INT_TMR_MATCH ELUA_INT_INVALID_INTERRUPT
# endif
# endif // # if defined ( BUILD_LUA_INT_HANDLERS ) || defined ( BUILD_C_INT_HANDLERS )
# ifndef VTMR_NUM_TIMERS
# define VTMR_NUM_TIMERS 0
# endif // # ifndef VTMR_NUM_TIMERS
# ifndef CON_BUF_SIZE
# define CON_BUF_SIZE 0
# endif // # ifndef CON_BUF_SIZE
# ifndef SERMUX_FLOW_TYPE
# define SERMUX_FLOW_TYPE PLATFORM_UART_FLOW_NONE
# endif
# ifndef CON_FLOW_TYPE
# define CON_FLOW_TYPE PLATFORM_UART_FLOW_NONE
# endif
# ifndef CON_TIMER_ID
# define CON_TIMER_ID PLATFORM_TIMER_SYS_ID
# endif
# ifdef ELUA_BOOT_RPC
# ifndef RPC_UART_ID
# define RPC_UART_ID CON_UART_ID
# endif
# ifndef RPC_TIMER_ID
# define RPC_TIMER_ID PLATFORM_TIMER_SYS_ID
# endif
# ifndef RPC_UART_SPEED
# define RPC_UART_SPEED CON_UART_SPEED
# endif
# endif // # ifdef ELUA_BOOT_RPC
2012-07-01 12:19:19 +03:00
# if ( defined ( BUILD_RFS ) || defined ( BUILD_SERMUX ) || defined ( CON_BUF_SIZE ) ) && ! defined ( BUF_ENABLE_UART )
# define BUF_ENABLE_UART
# endif
2012-07-12 01:50:38 +03:00
# if defined ( ADC_BUF_SIZE ) && ! defined ( BUF_ENABLE_ADC )
# define BUF_ENABLE_ADC
# endif
2012-07-01 01:07:59 +03:00
] ]
-------------------------------------------------------------------------------
-- Public interface
-- This code is executed an initialization time (the first 'require')
components = comps.init ( )
configs = cfgs.init ( )
-- Read the Lua description file of a board and return the corresponding header file
-- as a string or (false, error) if an error occured
function compile_board ( fname , boardname )
local cboardname = boardname : upper ( ) : gsub ( " [%-%.%s] " , " _ " )
local header = sf ( [ [
// Lua board configuration file , automatically generated
# ifndef __GENERATED_ % s_H__
# define __GENERATED_ % s_H__
] ] , cboardname , cboardname )
local desc , err = dofile ( fname )
if not desc then return false , err end
if not desc.cpu then return false , " cpu not specified in board configuration file " end
2012-07-01 12:19:19 +03:00
-- Check the keys in 'desc'
2012-07-01 20:47:33 +03:00
local known_keys = { ' cpu ' , ' components ' , ' config ' , ' headers ' , ' macros ' , ' modules ' , ' cpu_constants ' , ' allocator ' , ' target ' }
2012-07-01 12:19:19 +03:00
for k , _ in pairs ( desc ) do
if not utils.array_element_index ( known_keys , k ) then return false , sf ( " unknown key '%s' " , k ) end
end
2012-07-01 20:47:33 +03:00
-- Check CPU
local cpulist = bd.get_all_cpus ( )
if not utils.array_element_index ( cpulist , desc.cpu : upper ( ) ) then
return false , sf ( " unknown cpu '%s' " , desc.cpu )
end
2012-07-01 01:07:59 +03:00
-- Find and require the platform board configuration file
local platform = bd.get_platform_of_cpu ( desc.cpu )
if not platform then return false , sf ( " unable to find the platform of cpu '%s' " , desc.cpu ) end
2012-07-01 20:47:33 +03:00
local plconf = default_platform_conf
2012-07-12 01:50:38 +03:00
if utils.is_file ( utils.concat_path { ' src ' , ' platform ' , platform , ' build_config.lua ' } ) then
plconf = require ( " src.platform. " .. platform .. " .build_config " )
2012-07-01 20:47:33 +03:00
print ( utils.col_blue ( sf ( " [CONFIG] Found a backend build configuration file for platform %s " , platform ) ) )
2012-07-01 01:07:59 +03:00
end
-- Read platform specific components/configs
plconf.add_platform_components ( components )
plconf.add_platform_configs ( configs )
-- Do we need to include any configured headers?
if type ( desc.headers ) == " table " and # desc.headers > 0 then
for _ , h in pairs ( desc.headers ) do
header = header .. " #include " .. h .. " \n "
end
header = header .. " \n "
end
-- Do we need to add definitions for any configured macros?
if type ( desc.macros ) == " table " and # desc.macros > 0 then
for _ , m in pairs ( desc.macros ) do
if type ( m ) == " string " then -- #define m
header = header .. gen.print_define ( m )
elseif type ( m ) == " table " then -- { macro, value } -> #define macro value
header = header .. gen.print_define ( m [ 1 ] , m [ 2 ] )
end
end
header = header .. " \n "
end
-- Generate components first
local gen , err = generate_components ( desc , plconf )
if not gen then return false , err end
header = header .. gen
2012-07-01 20:47:33 +03:00
-- Keep generated data for later use
glconf , glen = sects.conf , sects.enabled
2012-07-01 01:07:59 +03:00
-- Then configs
gen , err = generate_config ( desc , plconf )
local multi_alloc = sects.conf . use_multiple_allocator
if not gen then return false , err end
header = header .. gen
2012-07-01 20:47:33 +03:00
-- Accumulate generated data into 'glconf' and 'glen'
utils.concat_tables ( glconf , sects.conf )
utils.concat_tables ( glen , sects.enabled )
-- Now we have all components and the configuration generated
-- It's a good time for some sanity checks
gen , err = check_components_and_config ( )
if not gen then return false , err end
2012-07-01 01:07:59 +03:00
-- Now it's a good time to include the fixed sanity checks
header = header .. sanity_code
-- Generate module configuration
gen , err = mgen.gen_module_list ( desc , plconf , platform )
if not gen then return false , err end
header = header .. gen
-- Generate additional CPU constants
gen , err = cpuct.gen_constants ( desc )
if not gen then return false , err end
header = header .. gen
-- All done, write the header's footer
header = header .. sf ( " #endif // #ifndef __GENERATED_%s_H__ \n " , cboardname )
2012-07-01 20:47:33 +03:00
-- We are done with the header, we still need to check the compile flags
local allocator
if desc.allocator then
local allocs = { ' newlib ' , ' multiple ' , ' simple ' }
if not utils.array_element_index ( allocs , desc.allocator ) then
return false , sf ( " unknown allocator '%s' " , desc.allocator )
end
allocator = desc.allocator
end
local target
if desc.target then
local targets = { ' lua ' , ' lualong ' , ' lualonglong ' }
if not utils.array_element_index ( targets , desc.target ) then
return false , sf ( " unknown target '%ws' " , desc.target )
end
target = desc.target
end
2012-07-01 01:07:59 +03:00
-- Return the contents of the header, as well as the name of the CPU used by this
2012-07-01 20:47:33 +03:00
-- board (this information is needed by the builder) and the build information
return { header = header , cpu = desc.cpu , multi_alloc = multi_alloc , allocator = allocator , target = target }
2012-07-01 01:07:59 +03:00
end