diff --git a/build/attributes.lua b/build/attributes.lua new file mode 100644 index 00000000..438d8bfb --- /dev/null +++ b/build/attributes.lua @@ -0,0 +1,115 @@ +-- Attributes are used by various parts of the board description +-- system (for example configs and components) + +module( ..., package.seeall ) +local ct = require "constants" +local sf = string.format + +-- Validator for a 'choice' attribute +-- Returns the value if OK, (false, errmsg) for error +-- Needs: attrvals - list of permitted values for this attribute +local function validate_choice( adesc, aname, aval, comp ) + aval = aval:lower() + for k, v in pairs( adesc.attrvals ) do + if v == aval then return v end + end + return false, sf( "invalid value '%s' for attribute '%s' of component '%s'\n", aval, aname, comp ) +end + +-- Validator for a 'number' attribute +-- Returns the value if OK, (false, errmsg) for error +-- Needs: attrtype - the number type ('int' or 'float') +-- attrmin - minimum value (no minimum check will be performed if not specified) +-- attrmax - maximum value (no maximum check will be performed if not specified) +local function validate_number( adesc, aname, aval, comp ) + aval = tonumber( aval ) + if not aval then return false, sf( "value of attribute '%s' for component '%s' must be a number", aname, comp ) end + if adesc.attrtype == 'int' and math.floor( aval ) ~= aval then + return false, sf( "value of attribute '%s' for component '%s' must be an integer", aname, comp ) + end + local minval = adesc.attrmin or aval + local maxval = adesc.attrmax or aval + if aval < minval then + return false, sf( "value of attribute '%s' for component '%s' must be larger than '%s'\n", aname, comp, tostring( minval ) ) + end + if aval > maxval then + return false, sf( "value of attribute '%s' for component '%s' must be smaller than '%s'\n", aname, comp, tostring( maxval ) ) + end + return aval +end + +-- Validator for a log2 number attribute +-- Works like number, but additionaly checks that the value is a power of 2 +-- Also changes the attribute value to its log2 +local function validate_log2( adesc, aname, aval, comp ) + local res, err = validate_number( adesc, aname, aval, comp ) + if not res then return res, err end + if aval <= 0 then + return false, sf( "value of attribute '%s' for component '%s' must be larger than 0\n", aname, comp ) + end + local thelog = math.log( res ) / math.log( 2 ) + if thelog ~= math.floor( thelog ) then + return false, sf( "value of attribute '%s' for component '%s' must be a power of 2\n", aname, comp ) + end + return thelog +end + +-- Validator for a string attribute +-- Return the string if OK, (false, errmsg) for error +-- Needs: attrmaxsize - maximum size of the string +local function validate_string( adesc, aname, aval, comp ) + aval = tostring( aval ) + if type( aval ) ~= "string" then + return false, sf( "value of attribute '%s' for component '%s' must be a string", aname, comp ) + end + maxsize = adesc.attrmaxsize or math.huge + if #aval > adesc.attrmaxsize then + return false, sf( "value of attribute '%s' for component '%s' must be less than %d chars in length", aname, comp, maxsize ) + end + return aval +end + +------------------------------------------------------------------------------- +-- Public interface + +-- Returns a new timer attribute with the given macro and default (systmr if not specified) +function timer_attr( macro, default ) + default = default or ct.systmr + return { macro = macro, default = default } +end + +-- Returns a new integer number attribute with the given limits +function int_attr( macro, minv, maxv, default ) + return { macro = macro, default = default, validator = validate_number, attrtype = 'int', attrmin = minv, attrmax = maxv } +end + +-- Returns a new integer number attribute with the given limits +-- The generated value will be the integer's base 2 logarithm +function int_log2_attr( macro, minv, maxv, default ) + local t = int_attr( macro, minv, maxv, default ) + t.validator = validate_log2 + return t +end + +-- Returns a new choice attribute with the given possible values +function choice_attr( macro, values, default ) + return { macro = macro, default = default, validator = validate_choice, attrvals = values } +end + +-- Returns a new flow control attribute +function flow_control_attr( macro, default ) + default = default or ct.uart_flow.none + return choice_attr( macro, ct.uart_flow_vals, default ) +end + +-- Returns a new string attribute +function string_attr( macro, maxsize, dfault ) + return { macro = macro, default = default, validator = validate_string, attrmaxsize = maxsize } +end + +-- Make the given attribute optional +function make_optional( attr ) + attr.optional = true + return attr +end + diff --git a/build/builder.lua b/build/builder.lua index 8e5ec403..42084318 100644 --- a/build/builder.lua +++ b/build/builder.lua @@ -36,14 +36,35 @@ return { } --]] +--[[ platform_conf.h template +#include various files (buf, type...) + +#include ELUA_CPU_HEADER +#include ELUA_BOARD_HEADER +#include "platform_post_config.h" +#include "platform_generic.h" +#include "platform_interrupts.h" +--]] + local test = { - cpu = 'lm3s8962', + cpu = 'stm32f103', + config = { + extram = { start = 0, size = 256 * 1024 }, + vtmr = { num = 4, freq = 10 }, + egc = { mode = "full", limit = 40 * 1024 } + }, components = { wofs = true, romfs = true, shell = { uart = 0, speed = 115200 }, - xmodem = true + xmodem = true, + term = { lines = 25, cols = 80 }, + cints = true, + luaints = { queue_size = 32 }, + linenoise = { shell_lines = 10, lua_lines = 50 }, + rfs = { uart = 1, speed = 115200, buf_size = 512 } } } print( comp.gen_config( test ) ) + diff --git a/build/component.lua b/build/component.lua index fb3eac9d..2c711d7a 100644 --- a/build/component.lua +++ b/build/component.lua @@ -5,13 +5,17 @@ module( ..., package.seeall ) local sf = string.format local ct = require "constants" +local at = require "attributes" +local gen = require "generators" + local components = {} local conf = {} local enabled = {} -local MACRO_DEF_POS = 41 +------------------------------------------------------------------------------- +-- Various helper functions --- Enables and configures this component +-- Enables and configures the given component local function config( name, data ) local desc = components[ name ] local attrs = desc.attrs or {} @@ -20,29 +24,36 @@ local function config( name, data ) local elmeta = attrs[ attr ] if not elmeta then error( sf( "attribute '%s' is not defined for component '%s'", attr, name ) ) end if elmeta.validator then - if not elmeta:validator( v ) then + local res, err = elmeta:validator( attr, v, name ) + if not res then error( sf( "'%s' is not a valid value for attribute '%s' of component '%s'", v, attr, name ) ) + else + -- The validator can also change the attribute's value + v = res end end - if conf[ elmeta.macro ] then + if conf[ elmeta.macro ] and tostring( conf[ elmeta.macro ].value ) ~= tostring( v ) then print( sf( "WARNING: overriding value of '%s' from '%s' to '%s' with component '%s'", elmeta.macro, conf[ elmeta.macro ].value, v, name ) ) end conf[ elmeta.macro ] = { desc = elmeta, value = v } - print( sf( "SET -> '%s' = '%s'", elmeta.macro, v ) ) + -- print( sf( "SET -> '%s' = '%s'", elmeta.macro, v ) ) end -- Set default values where needed for name, data in pairs( attrs ) do - if not conf[ name ] and data.default then + if not conf[ data.macro ] and data.default then conf[ data.macro ] = { desc = data, value = data.default } - print( sf( "DEFAULT -> '%s' = '%s'", data.macro, data.default ) ) + -- print( sf( "DEFAULT -> '%s' = '%s'", data.macro, data.default ) ) end end -- Mark this component as configured enabled[ name ] = true - print( sf( "ENABLED -> %s", name ) ) + -- print( sf( "ENABLED -> %s", name ) ) end +------------------------------------------------------------------------------- +-- Configuration checkers + -- Helper: shell configuration consistency checker -- Returns 'true' if consistent, (false, errmsg) otherwise local function shell_confcheck( compdesc, vals ) @@ -63,27 +74,23 @@ local function shell_confcheck( compdesc, vals ) return true end --- Returns a new timer option with the given macro and default (systmr if not specified) -local function timer_attr( macro, default ) - default = default or ct.systmr - return { macro = macro, default = default, gen = TODO_tmr_gen } -end +------------------------------------------------------------------------------- +-- Component-specific generators --- Formatted print for "#define" -local function print_define( k, v ) - v = v or '' - local s = sf( "#define %s", k:upper() ) - if v then - if #s < MACRO_DEF_POS then s = s .. string.rep( ' ', MACRO_DEF_POS - #s ) end +-- Generator for the shell component +local function shell_gen( desc, conf, generated ) + generated._SHELL_TRANSPORT = true + local gstr = '' + local shtype = conf._SHELL_TRANSPORT.value + if shtype == 'serial' then + gstr = gstr .. gen.simple_gen( "CON_UART_ID", conf, generated ) + gstr = gstr .. gen.simple_gen( "CON_UART_SPEED", conf, generated ) + gstr = gstr .. gen.simple_gen( "CON_TIMER_ID", conf, generated ) + gstr = gstr .. gen.simple_gen( "CON_FLOW_TYPE", conf, generated ) + else + error "TODO: implement shell over TCP/IP generator" end - s = s .. v .. "\n" - return s -end - --- Simple generator for an attribute -local function simple_gen( attrname ) - local adesc, aval = conf[ attrname ].desc, conf[ attrname ].value - return print_define( attrname, aval ) + return gstr end ------------------------------------------------------------------------------- @@ -91,15 +98,26 @@ end -- Build all components needed by eLua, save them in the "components" table function init() + -- Serial console + components.sercon = { + macro = 'BUILD_CON_GENERIC', + attrs = { + uart = at.int_attr( 'CON_UART_ID' ), + speed = at.int_attr( 'CON_UART_SPEED' ), + timer = at.timer_attr( 'CON_TIMER_ID' ), + flow = at.flow_control_attr( 'CON_FLOW_TYPE' ) + } + } + -- TCP/IP console + components.tcpipcon = { macro = 'BUILD_CON_TCP' } -- XMODEM components.xmodem = { macro = 'BUILD_XMODEM', - attrs = - { - uart = { macro = 'CON_UART_ID' }, - speed = { macro = 'CON_UART_SPEED' }, - timer = timer_attr( 'CON_TIMER_ID' ), - flow = { macro = 'CON_FLOW_TYPE', default = ct.uart_flow.none } + attrs = { + uart = at.int_attr( 'CON_UART_ID' ), + speed = at.int_attr( 'CON_UART_SPEED' ), + timer = at.timer_attr( 'CON_TIMER_ID' ), + flow = at.flow_control_attr( 'CON_FLOW_TYPE') } } -- Shell @@ -107,13 +125,54 @@ function init() macro = 'BUILD_SHELL', confcheck = shell_confcheck, gen = shell_gen, - attrs = - { - transport = { macro = '_SHELL_TRANSPORT', default = 'serial' }, - uart = { macro = 'CON_UART_ID' }, - speed = { macro = 'CON_UART_SPEED' }, - timer = timer_attr( 'CON_TIMER_ID' ), - flow = { macro = 'CON_FLOW_TYPE', default = ct.uart_flow.none } + attrs = { + transport = at.choice_attr( '_SHELL_TRANSPORT', { 'serial', 'tcpip' }, 'serial' ), + uart = at.int_attr( 'CON_UART_ID' ), + speed = at.int_attr( 'CON_UART_SPEED' ), + timer = at.timer_attr( 'CON_TIMER_ID' ), + flow = at.flow_control_attr( 'CON_FLOW_TYPE' ) + } + } + -- Term + components.term = { + macro = 'BUILD_TERM', + attrs = { + uart = at.int_attr( 'CON_UART_ID' ), + speed = at.int_attr( 'CON_UART_SPEED' ), + timer = at.timer_attr( 'CON_TIMER_ID' ), + flow = at.flow_control_attr( 'CON_FLOW_TYPE' ), + lines = at.int_attr( 'TERM_LINES' ), + cols = at.int_attr( 'TERM_COLS' ) + } + } + -- C interrupt support + components.cints = { macro = 'BUILD_C_INT_HANDLERS' } + -- Lua interrupt support + components.luaints = { + macro = 'BUILD_LUA_INT_HANDLERS', + attrs = { + queue_size = at.int_log2_attr( 'PLATFORM_INT_QUEUE_LOG_SIZE', nil, nil, 32 ) + } + } + -- Linenoise + components.linenoise = { + macro = 'BUILD_LINENOISE', + attrs = { + shell_lines = at.int_attr( 'LINENOISE_HISTORY_SIZE_SHELL' ), + lua_lines = at.int_attr( 'LINENOISE_HISTORY_SIZE_LUA' ), + autosave_file = at.make_optional( at.string_attr( 'LINENOISE_AUTOSAVE_FNAME', '', 32 ) ) + } + } + -- RFS + components.rfs = { + macro = 'BUILD_RFS', + attrs = { + uart = at.int_attr( 'RFS_UART_ID' ), + speed = at.int_attr( 'RFS_UART_SPEED' ), + timer = at.timer_attr( 'RFS_TIMER_ID' ), + flow = at.flow_control_attr( 'RFS_FLOW_TYPE' ), + buf_size = at.int_log2_attr( 'RFS_BUFFER_SIZE', 512 ), + timeout = at.int_attr( 'RFS_TIMEOUT', nil, nil, 100000 ) } } -- ROMFS @@ -125,18 +184,28 @@ end -- Generate configuration data starting from the input dictionary function gen_config( d ) conf, enabled = {}, {} + local comps = d.components + + -- Prerequisites: check for keys that might be needed in 'd', but might not be there + -- At the moment, we need to definer either BUILD_CON_GENERIC or BUILD_CON_TCP (but not both) + -- If none is defined, we default to BUILD_CON_GENERIC + -- If both are defined, we exit with error + if comps.sercon and comps.tcpipcon then + return nil, "serial and TCP/IP console can't be enabled at the same time" + elseif not comps.sercon and not comps.tcpipcon then + comps.sercon = true + end -- Step 1: interpret data in the input table - local comps = d.components if not comps then return false, "unable to find components in the board description" end -- Configure each component in turn, doing validation as required for compname, compval in pairs( comps ) do if not components[ compname ] then error( sf( "unknown component '%s'", compname ) ) end - if type( compval ) ~= "table" then + if type( compval ) ~= "table" and compval then comps[ compname ] = {} compval = comps[ compname ] end - config( compname, compval ) + if compval then config( compname, compval ) end end -- Step 2: basic consistency check @@ -160,7 +229,7 @@ function gen_config( d ) -- Step 3: actual generation of code -- The default generator simply adds '#define KEY VALUE' pairs. A component can overwrite this - -- default verification by specifying its own 'gen' function + -- default generation by specifying its own 'gen' function -- Also, we never generate the same key twice. We ensure this by keeping a table of the -- keys that were already generated local generated = {} @@ -172,18 +241,16 @@ function gen_config( d ) if desc.gen then genstr = genstr .. desc:gen( conf, generated ) else - for aname, adesc in pairs( attrs ) do - if not generated[ adesc.macro ] then - genstr = genstr .. simple_gen( adesc.macro ) - generated[ adesc.macro ] = true - end + for aname, adesc in pairs( attrs ) do + genstr = genstr .. gen.simple_gen( adesc.macro, conf, generated ) end end -- Add the "build enable" macro - genstr = genstr .. print_define( desc.macro ) .. "\n" + if desc.macro then genstr = genstr .. gen.print_define( desc.macro ) .. "\n" end end - -- Step 4: take care of all "special cases" + -- Step 4: take care of all "special cases" (if any) + -- TODO: check proper sermux ID assignment for console/RFS -- All done return genstr diff --git a/build/constants.lua b/build/constants.lua index 7e86bf49..cc2b28dd 100644 --- a/build/constants.lua +++ b/build/constants.lua @@ -12,6 +12,11 @@ uart_flow = rtscts = '( PLATFORM_UART_FLOW_RTS | PLATFORM_UART_FLOW_CTS )' } +uart_flow_vals = {} +for k, v in pairs( uart_flow ) do + uart_flow_vals[ #uart_flow_vals + 1 ] = v +end + -- System timer ID systmr = 'PLATFORM_TIMER_SYS_ID' @@ -20,3 +25,8 @@ for i = 0, 31 do _G[ sf( 'vtmr%d', i ) ] = sf( '( VTMR_FIRST_ID + %d )', i ) end +-- Add a sufficient number of virtual UARTs +for i = 0, 31 do + _G[ sf( 'vuart%d', i ) ] = sf( '( SERMUX_SERVICE_ID_FIRST + %d )', i ) +end + diff --git a/build/generators.lua b/build/generators.lua new file mode 100644 index 00000000..b80ea85f --- /dev/null +++ b/build/generators.lua @@ -0,0 +1,27 @@ +-- Code generators for various attributes and other constructs + +module( ..., package.seeall ) +local sf = string.format + +local MACRO_DEF_POS = 41 + +-- Formatted print for "#define" +function print_define( k, v ) + v = v or '' + local s = sf( "#define %s", k:upper() ) + if v then + if #s < MACRO_DEF_POS then s = s .. string.rep( ' ', MACRO_DEF_POS - #s ) end + end + s = s .. v .. "\n" + return s +end + +-- Simple generator for an attribute +function simple_gen( attrname, conf, gentable ) + if gentable[ attrname ] then return '' end + if not conf[ attrname ] then return '' end + local adesc, aval = conf[ attrname ].desc, conf[ attrname ].value + gentable[ attrname ] = true + return print_define( attrname, aval ) +end + diff --git a/src/platform/lpc24xx/lpc2468.h b/src/platform/lpc24xx/lpc2468.h new file mode 100644 index 00000000..2c820c00 --- /dev/null +++ b/src/platform/lpc24xx/lpc2468.h @@ -0,0 +1,35 @@ +// LPC2468 CPU definitions + +#ifndef __LPC2468_H__ +#define __LPC2468_H__ + +#include "target.h" + +// Number of resources (0 if not available/not implemented) +#define NUM_PIO 5 +#define NUM_SPI 0 +#define NUM_UART 4 +#define NUM_PWM 12 +#define NUM_ADC 8 +#define NUM_CAN 0 +#define NUM_TIMER 4 + +// CPU frequency (needed by the CPU module and MMCFS code, 0 if not used) +#define CPU_FREQUENCY Fcclk + +// PIO prefix ('0' for P0, P1, ... or 'A' for PA, PB, ...) +#define PIO_PREFIX '0' +// Pins per port configuration: +// #define PIO_PINS_PER_PORT (n) if each port has the same number of pins, or +// #define PIO_PIN_ARRAY { n1, n2, ... } to define pins per port in an array +// Use #define PIO_PINS_PER_PORT 0 if this isn't needed +#define PIO_PINS_PER_PORT 32 + +#define SRAM_ORIGIN 0x40000000 +#define SRAM_SIZE 0x10000 // [TODO]: make this 96k? + +#define INTERNAL_FLASH_START_ADDRESS 0 +#define INTERNAL_FLASH_SIZE ( 512 * 1024 ) + +#endif // #ifndef __LPC2468_H__ + diff --git a/src/platform/stm32/stm32f103.h b/src/platform/stm32/stm32f103.h new file mode 100644 index 00000000..46601846 --- /dev/null +++ b/src/platform/stm32/stm32f103.h @@ -0,0 +1,40 @@ +// CPU definition file for STM32F103 + +#ifndef __STM32F103_H__ +#define __STM32F103_H__ + +#include "type.h" + +// Number of resources (0 if not available/not implemented) +#define NUM_PIO 7 +#define NUM_SPI 2 +#define NUM_UART 5 +#define NUM_TIMER 5 +#define NUM_PHYS_TIMER 5 +#define NUM_PWM 4 +#define NUM_ADC 16 +#define NUM_CAN 1 + +u32 platform_s_cpu_get_frequency(); +#define CPU_FREQUENCY platform_s_cpu_get_frequency() + +// PIO prefix ('0' for P0, P1, ... or 'A' for PA, PB, ...) +#define PIO_PREFIX 'A' +// Pins per port configuration: +// #define PIO_PINS_PER_PORT (n) if each port has the same number of pins, or +// #define PIO_PIN_ARRAY { n1, n2, ... } to define pins per port in an array +// Use #define PIO_PINS_PER_PORT 0 if this isn't needed +#define PIO_PINS_PER_PORT 16 + +// Allocator data: define your free memory zones here in two arrays +// (start address and end address) +#define SRAM_SIZE ( 64 * 1024 ) +#define MEM_START_ADDRESS { ( void* )end } +#define MEM_END_ADDRESS { ( void* )( SRAM_BASE + SRAM_SIZE - STACK_SIZE_TOTAL - 1 ) } + +#define INTERNAL_FLASH_SIZE ( 512 * 1024 ) +#define INTERNAL_FLASH_SECTOR_SIZE 2048 +#define INTERNAL_FLASH_START_ADDRESS 0x08000000 + +#endif //#ifndef __STM32F103_H__ +