Alexander.Steffen@infineon.com
ese19.asdn.eu
The C language combines all the power of assembly language with all the ease-of-use of assembly language.
Abnahmetest |
Systemtest |
Integrationstest |
Komponententest |
!cat add.h
int add(int a, int b);
!cat add.c
#include "add.h" int add(int a, int b) { return a + b; }
class AddTest(unittest.TestCase):
def test_addition(self):
module = load('add')
self.assertEqual(module.add(1, 2), 1 + 2)
run(AddTest)
test_addition (__main__.AddTest) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.128s OK
def load(filename):
# load source code
source = open(filename + '.c').read()
includes = open(filename + '.h').read()
# pass source code to CFFI
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(filename + '_', source)
ffibuilder.compile()
# import and return resulting module
module = importlib.import_module(filename + '_')
return module.lib
!cat sum.h
int sum(int a);
!cat sum.c
#include "sum.h" static int _sum = 0; int sum(int a) { _sum += a; return _sum; }
class SumTest(unittest.TestCase):
def setUp(self):
self.module = load('sum')
def test_zero(self):
self.assertEqual(self.module.sum(0), 0)
def test_one(self):
self.assertEqual(self.module.sum(1), 1)
def test_multiple(self):
self.assertEqual(self.module.sum(2), 2)
self.assertEqual(self.module.sum(4), 2 + 4)
run(SumTest)
test_multiple (__main__.SumTest) ... ok test_one (__main__.SumTest) ... ok test_zero (__main__.SumTest) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.320s OK
def load(filename):
source = open(filename + '.c').read()
includes = open(filename + '.h').read()
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(filename + '_', source)
ffibuilder.compile()
module = importlib.import_module(filename + '_')
return module.lib
def load(filename):
# generate random name
name = filename + '_' + uuid.uuid4().hex
source = open(filename + '.c').read()
includes = open(filename + '.h').read()
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(name, source)
ffibuilder.compile()
module = importlib.import_module(name)
return module.lib
!cat types.h
typedef struct { float real; float imaginary; } complex;
!cat complex.h
#include "types.h" complex add(complex a, complex b);
!cat complex.c
#include "complex.h" complex add(complex a, complex b) { a.real += b.real; a.imaginary += b.imaginary; return a; }
class ComplexTest(unittest.TestCase):
def setUp(self):
self.module = load('complex')
def test_addition(self):
result = self.module.add([0, 1], [2, 3])
self.assertAlmostEqual(result.real, 2)
self.assertAlmostEqual(result.imaginary, 4)
run(ComplexTest)
test_addition (__main__.ComplexTest) ... ok ---------------------------------------------------------------------- Ran 1 test in 0.145s OK
def load(filename):
name = filename + '_' + uuid.uuid4().hex
source = open(filename + '.c').read()
includes = open(filename + '.h').read()
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(name, source)
ffibuilder.compile()
module = importlib.import_module(name)
return module.lib
def load(filename):
name = filename + '_' + uuid.uuid4().hex
source = open(filename + '.c').read()
# handle preprocessor directives
includes = preprocess(open(filename + '.h').read())
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(name, source)
ffibuilder.compile()
module = importlib.import_module(name)
return module.lib
def preprocess(source):
return subprocess.run(['gcc', '-E', '-P', '-'],
input=source, stdout=subprocess.PIPE,
universal_newlines=True, check=True).stdout
!cat gpio_lib.h
int read_gpio0(void); int read_gpio1(void);
!cat gpio.h
int read_gpio(int number);
!cat gpio.c
#include "gpio.h" #include "gpio_lib.h" int read_gpio(int number) { switch (number) { case 0: return read_gpio0(); case 1: return read_gpio1(); default: return -1; } }
class GPIOTest(unittest.TestCase):
def setUp(self):
self.module, self.ffi = load2('gpio')
def test_read_gpio0(self):
@self.ffi.def_extern()
def read_gpio0():
return 42
self.assertEqual(self.module.read_gpio(0), 42)
def test_read_gpio1(self):
read_gpio1 = unittest.mock.MagicMock(return_value=21)
self.ffi.def_extern('read_gpio1')(read_gpio1)
self.assertEqual(self.module.read_gpio(1), 21)
read_gpio1.assert_called_once_with()
run(GPIOTest)
test_read_gpio0 (__main__.GPIOTest) ... ok test_read_gpio1 (__main__.GPIOTest) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.277s OK
def load(filename):
name = filename + '_' + uuid.uuid4().hex
source = open(filename + '.c').read()
includes = preprocess(open(filename + '.h').read())
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(name, source)
ffibuilder.compile()
module = importlib.import_module(name)
return module.lib
def load2(filename):
name = filename + '_' + uuid.uuid4().hex
source = open(filename + '.c').read()
# preprocess all header files for CFFI
includes = preprocess(''.join(re.findall('\s*#include\s+.*', source)))
# prefix external functions with extern "Python+C"
local_functions = FunctionList(preprocess(source)).funcs
includes = convert_function_declarations(includes, local_functions)
ffibuilder = cffi.FFI()
ffibuilder.cdef(includes)
ffibuilder.set_source(name, source)
ffibuilder.compile()
module = importlib.import_module(name)
# return both the library object and the ffi object
return module.lib, module.ffi
class FunctionList(pycparser.c_ast.NodeVisitor):
def __init__(self, source):
self.funcs = set()
self.visit(pycparser.CParser().parse(source))
def visit_FuncDef(self, node):
self.funcs.add(node.decl.name)
class CFFIGenerator(pycparser.c_generator.CGenerator):
def __init__(self, blacklist):
super().__init__()
self.blacklist = blacklist
def visit_Decl(self, n, *args, **kwargs):
result = super().visit_Decl(n, *args, **kwargs)
if isinstance(n.type, pycparser.c_ast.FuncDecl):
if n.name not in self.blacklist:
return 'extern "Python+C" ' + result
return result
def convert_function_declarations(source, blacklist=set()):
return CFFIGenerator(blacklist).visit(pycparser.CParser().parse(source))
Abnahmetest |
Systemtest |
Integrationstest |
Komponententest |
Applikation |
HAL |
Applikation |
Python |
![ -e micropython ] || git clone https://github.com/micropython/micropython -b v1.11
cat application/*.c | ||||||
→ | CFFI | → | gcc | → | python | |
cat hal/*.h |
source_files = [
'micropython/ports/minimal/main.c',
'micropython/ports/minimal/build/_frozen_mpy.c',
'micropython/lib/utils/pyexec.c',
'micropython/lib/mp-readline/readline.c',
]
source_files.extend(glob.glob('micropython/py/*.c'))
cat application/*.c | ||||||
→ | CFFI | → | gcc | → | python | |
cat hal/*.h |
header_files = {os.path.join('micropython', 'py', 'mphal.h')}
header_content = '#define __attribute__(x)\n#define mp_hal_pin_obj_t void*\n'
header_content += ''.join(open(f).read() for f in header_files)
header_content = preprocess(header_content)
sorted(header_content.split('\n'))[-3:]
['void mp_hal_stdout_tx_str(const char *str);', 'void mp_hal_stdout_tx_strn(const char *str, size_t len);', 'void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len);']
class CFFIGenerator(pycparser.c_generator.CGenerator):
def __init__(self, blacklist):
super().__init__()
self.blacklist = blacklist
def visit_Decl(self, n, *args, **kwargs):
result = super().visit_Decl(n, *args, **kwargs)
if isinstance(n.type, pycparser.c_ast.FuncDecl):
if n.name not in self.blacklist:
return 'extern "Python+C" ' + result
return result
def convert_function_declarations(source, blacklist=set()):
return CFFIGenerator(blacklist).visit(pycparser.CParser().parse(source))
class CFFIGenerator2(CFFIGenerator):
def visit_FuncDef(self, n, *args, **kwargs):
# remove definitions of inline functions
return ''
CFFIGenerator = CFFIGenerator2
header_content = convert_function_declarations(header_content)
header_content += 'int mpmain(int argc, char **argv);'
sorted(header_content.split('\n'))[6:9]
['extern "Python+C" void mp_hal_delay_us(mp_uint_t us);', 'extern "Python+C" void mp_hal_stdout_tx_str(const char *str);', 'extern "Python+C" void mp_hal_stdout_tx_strn(const char *str, size_t len);']
cat application/*.c | ||||||
→ | CFFI | → | gcc | → | python | |
cat hal/*.h |
include_dirs = [
'micropython',
'micropython/ports/minimal',
'micropython/ports/minimal/build',
]
ffibuilder = cffi.FFI()
ffibuilder.cdef(header_content)
ffibuilder.set_source(
module_name='mpsim',
source='',
sources=source_files,
include_dirs=include_dirs,
define_macros=[('main', 'mpmain')],
)
ffibuilder.compile();
cat application/*.c | ||||||
→ | CFFI | → | gcc | → | python | |
cat hal/*.h |
import mpsim
@mpsim.ffi.def_extern()
def mp_hal_stdin_rx_chr():
return ord(sys.stdin.read(1))
@mpsim.ffi.def_extern()
def mp_hal_stdout_tx_strn(data, length):
print(bytes(mpsim.ffi.buffer(data, length)).decode(), end='', flush=True)
@mpsim.ffi.def_extern()
def mp_hal_stdout_tx_str(data):
print(mpsim.ffi.string(data).decode(), end='', flush=True)
$ ./demo.py MicroPython v1.11 on 2019-12-04; minimal with unknown-cpu >>> 1+1 2 >>> dir() ['__name__']
cat application/*.c | ||||||
→ | CFFI | → | gcc | → | python | |
cat hal/*.h |
$ ls -l bad/ -rw-r--r-- 1 user user 397789 1. Jan 1970 main.c
$ ls -l good/ drwxr-xr-x 2 user user 4096 1. Jan 1970 application drwxr-xr-x 2 user user 4096 1. Jan 1970 hal
struct { unsigned short major_version; unsigned int minor_version; } data; data.major_version = 1234; data.minor_version = 567890; checksum = sha256(&data, sizeof(data));
struct { uint16_t major_version; uint32_t minor_version; } __attribute__((packed)) data; data.major_version = htons(1234); data.minor_version = htonl(567890); checksum = sha256(&data, sizeof(data));
Applikation | Applikation | |
HAL | Python | |
Hardware | Code Netzwerk |
matplotlib.rcParams.update({'font.size': 20})
matplotlib.pyplot.figure(figsize=(10, 5))
matplotlib.pyplot.bar([1, 2], [5512/60, 334/60])
matplotlib.pyplot.ylabel('Laufzeit (Minuten)')
matplotlib.pyplot.xticks([1, 2], ('Hardware', 'Simulation'))
matplotlib.pyplot.show()
ffibuilder.set_source('mpsim', '', extra_compile_args=['-fsanitize-address'], libraries=['asan'])
os.environ['CC'] = 'afl-gcc'
import afl
stdin = sys.stdin.detach()
while afl.loop(10000):
application.lib.run(stdin.read())
Alexander.Steffen@infineon.com
ese19.asdn.eu