diff --git a/cmake/modules/OFS.cmake b/cmake/modules/OFS.cmake new file mode 100644 index 000000000000..43fda0f4ddb8 --- /dev/null +++ b/cmake/modules/OFS.cmake @@ -0,0 +1,53 @@ +#!/usr/bin/cmake -P +## Copyright(c) 2021, Intel Corporation +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and/or other materials provided with the distribution. +## * Neither the name of Intel Corporation nor the names of its contributors +## may be used to endorse or promote products derived from this software +## without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. + +macro(ofs_add_driver yml_file driver) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${driver}.h + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/ofs_parse.py ${CMAKE_CURRENT_LIST_DIR}/${yml_file} headers c ${CMAKE_CURRENT_BINARY_DIR} --driver ${driver} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS + ${CMAKE_CURRENT_LIST_DIR}/${yml_file} + ${CMAKE_SOURCE_DIR}/scripts/ofs_parse.py + ) + add_library(${driver} SHARED + ${CMAKE_CURRENT_BINARY_DIR}/${driver}.h + ${ARGN} + ) +target_include_directories(${driver} PUBLIC + $ + $ + $ + $ +) + +target_link_libraries(${driver} PUBLIC + opae-c +) +endmacro(ofs_add_driver yml_file) + + diff --git a/scripts/ofs_parse.py b/scripts/ofs_parse.py new file mode 100644 index 000000000000..6204c410eac0 --- /dev/null +++ b/scripts/ofs_parse.py @@ -0,0 +1,432 @@ +# Copyright(c) 2021, Intel Corporation +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# -*- coding: utf-8 -*- +# vim:fenc=utf-8 +import argparse +import ast +import io +import os +import re +import yaml + +from contextlib import contextmanager + +cpp_field_tmpl = '{spaces}{pod} f_{name} : {width};' +cpp_class_tmpl = ''' +#define {name}_OFFSET 0x{offset:0x} +union {name} {{ + enum {{ + offset = 0x{offset:0x} + }}; + {name}() {{}} + {name}({pod} v) : value(v) {{}} + {pod} value; + struct {{ +{fields} + }}; +}}; + +''' +c_struct_templ = ''' +#define {name}_OFFSET 0x{offset:0x} +typedef union _{name} {{ + {pod} value; + struct {{ +{fields} + }}; +}} {name}; + +''' + +driver_struct_templ = ''' +typedef struct _{driver} {{ + fpga_handle handle; +{members} +}} {driver}; + +int {driver}_init({driver} *{var}, fpga_handle h) +{{ + uint64_t *ptr = 0; + fpga_result res = fpgaMapMMIO(h, 0, &ptr); + if (res) {{ + return res; + }} + {var}->handle = h; +{inits} + return 0; +}} + +''' + +templates = {'c': c_struct_templ, + 'cpp': cpp_class_tmpl} + + +class ofs_field(object): + def __init__(self, name, bits, access, default, description): + self.name = name + self.bits = bits + self.access = access + self.default = default + self.description = description + + def max(self): + return max(self.bits) + + def min(self): + return min(self.bits) + + def __repr__(self): + return (f'{self.name}, {self.bits}, {self.access}, {self.default}, ' + f'{self.description}') + + def to_cpp(self, writer): + writer.write(cpp_field_tmpl.format(**vars(self))) + writer.write('\n') + for line in self.description.split('\n'): + writer.write(f'{self.spaces}// {line.strip()}\n') + + +class ofs_register(object): + def __init__(self, name, offset, default, description, fields=[]): + self.name = name + self.offset = offset + self.default = default + self.description = description + self.fields = [ofs_field(*f) for f in fields] + + def __repr__(self): + return (f'{self.name}, 0x{self.offset:0x}, 0x{self.default:0x}, ' + f'{self.description}, {self.fields}') + + def to_structure(self, tmpl, writer): + for line in self.description.split('\n'): + writer.writeline(f'// {line}') + writer.write(tmpl.lstrip().format(**vars(self))) + + +class RegisterTag(yaml.YAMLObject): + @classmethod + def from_yaml(cls, loader, node): + register_meta = loader.construct_sequence(node.value[0]) + fields = node.value[1].value if len(node.value) > 1 else () + register_fields = tuple(map(loader.construct_sequence, fields)) + return ofs_register(*register_meta, register_fields) + + +yaml.CLoader.add_constructor(u'tag:intel.com,2020:ofs/register', + RegisterTag.from_yaml) + + +def parse(fp): + return yaml.load(fp, Loader=yaml.CLoader) + + +class ofs_driver_writer(object): + def __init__(self, data): + self.data = data + + def write_header(self, output, language='c'): + self.name = self.data['name'] + self.registers = self.data['registers'] + self.functions = self.data.get('functions', {}) + filepath = os.path.join(output, f'{self.name}.h') + # with open(os.path.join(args.output, f'{name}.h'), 'w') as fp: + with ofs_header_writer.open(filepath, 'w') as writer: + writer.writeline( + f'// these structures were auto-generated using {__file__}') + writer.writeline( + '// modification of these structures may break the software\n') + if language == 'c': + writer.writeline(f'#ifndef __{self.name}__') + writer.writeline(f'#define __{self.name}__') + elif language == 'cpp': + writer.writeline('#pragma once') + writer.writeline('#include ') + writer.writeline('#include "ofs_primitives.h"') + + if language == 'c': + writer.writeline('\n#ifdef __cplusplus') + writer.writeline('extern "C" {') + writer.writeline('#endif\n') + + self.write_structures(writer, templates.get(language)) + self.write_driver(writer) + if language == 'c': + writer.writeline('\n#ifdef __cplusplus') + writer.writeline('}') + writer.writeline('#endif\n') + writer.writeline(f'#endif // __{self.name}__') + + def write_structures(self, fp, tmpl): + for r in self.registers: + r.width = 64 if max(r.fields, key=ofs_field.max).max() > 32 else 32 + r.pod = f'uint{r.width}_t' + r.fields = declare_fields(r.pod, r.fields, 4) + r.to_structure(tmpl, fp) + + def find_call(self, node): + if isinstance(node, ast.Call): + return node + if isinstance(node, ast.Return) or isinstance(node, ast.Expr): + if isinstance(node.value, ast.Call): + return node.value + + def annotation_to_c(self, node): + if isinstance(node, ast.Subscript): + return node.value.id, node.slice.value.value + return node.id, None + + def registers_names(self): + return [r.name for r in self.registers] + + def c_convert(self, body, node): + def arg_repl(m): + fn = m.group(1) + args = m.group(2) + if m.group(2) and m.group(2).strip(): + return f'{self.name}_{fn}(drv, {args})' + return f'{self.name}_{fn}(drv)' + # lines = body.split('\n') + line = ast.get_source_segment(body, node) + + call_node = self.find_call(node) + if call_node: + fn_name = call_node.func.id + if fn_name in self.functions: + line = re.sub(f'({fn_name})\\((.*?)\\)', arg_repl, line) + return self.ofs_resolve(node, self._c_convert(line)) + + def ofs_resolve(self, node, line): + if node is None: + return line + if isinstance(node, ast.Call): + func = node.func.id + arg0 = node.args[0].id + if func == 'ofs_addr' and arg0 in self.registers_names(): + arg1 = node.args[1].id + line = re.sub(f'ofs_addr\\(\\s*({arg0})\\s*,\\s*({arg1})\\s*\\)', # noqa + r'(\2*)drv->r_\1', line) + return line + if isinstance(node, ast.AnnAssign) or isinstance(node, ast.Assign): + # line = self.ofs_resolve(node.target, line) + line = self.ofs_resolve(node.value, line) + return line + if isinstance(node, ast.BinOp): + line = self.ofs_resolve(node.left, line) + line = self.ofs_resolve(node.right, line) + return line + if isinstance(node, ast.Name) and node.id in self.registers_names(): + line = line.replace(node.id, f'drv->r_{node.id}->value') + return line + + def find_registers(self, node): + if node is None: + return [] + if isinstance(node, ast.BinOp): + lhs = self.find_registers(node.left) + rhs = self.find_registers(node.right) + return lhs + rhs + value_id = getattr(node, 'id', None) + if value_id and value_id in self.registers_names(): + return [value_id] + return [] + + def _c_convert(self, line): + p = re.compile(r'(.*?)(?:(?P\w+)\.(?P\w+))(.*)') + m = p.match(line) + if m: + line = m.expand(r'\1drv->r_\2->f_\3\4') + f = re.compile(r'(\w+)\((.*?)\)') + m = f.match(line) + if m and m.group(1) in self.functions: + line = f.sub(f'{self.name}_\\1(drv, \\2)', line) + for py_syntax, c_syntax in [('not ', '!'), + (' and ', ' && '), + ('True', 'true'), + ('False', 'false')]: + line = line.replace(py_syntax, c_syntax) + return line + + def write_scoped_body(self, fp, body, indent=1): + spaces = ' '*indent + body = re.sub(r'#(.*)', r'__cmt__("\1")', body) + parsed = ast.parse(body) + lines = body.strip().split('\n') + for s in parsed.body: + if isinstance(s, ast.Expr) and isinstance(s.value, ast.Call): + line = self.c_convert(body, s) + if s.value.func.id == '__cmt__': + line = re.sub(r'__cmt__\("(.*?)"\)', r'//\1', line) + fp.writeline(f'{spaces}{line}') + elif type(s) in [ast.If, ast.While, ast.For]: + if isinstance(s, ast.For) and s.iter.func.id.endswith('range'): + i = s.target.id + _min = 0 if len(s.iter.args) == 1 else s.iter.args[0].id + _max = s.iter.args[-1].id + test = f'{i} = {_min}; i < {_max}; ++i' + keyword = 'for' + else: + test = self.c_convert(body, s.test) + keyword = 'if' if isinstance(s, ast.If) else 'while' + fp.write(f'{spaces}{keyword} ({test}) {{\n') + inner = io.StringIO() + first = s.body[0] + beg = first.lineno-1 + for line in lines[beg:]: + if line[:first.col_offset].strip() == '': + inner.write(f'{line[first.col_offset:]}\n') + else: + break + self.write_scoped_body(fp, inner.getvalue(), indent+1) + fp.write(f'{spaces}}}\n') + else: + line = self.c_convert(body, s) + if isinstance(s, ast.AnnAssign): + _type, size = self.annotation_to_c(s.annotation) + _var = s.target.id + if _type.endswith('_ptr'): + _type = _type.replace('_ptr', '') + line = re.sub( + f'({_var}):\\s*({_type})(?:_ptr)([{size}])?', + r'\2 *\1\3', line + ) + else: + line = re.sub( + f'({_var}):\\s*({_type})([{size}])?', + r'\2 \1\3', line + ) + # if _type.endswith('_ptr'): + # _type = _type.rstrip('_ptr') + # _var = f'\\*{_var}' + fp.write(f'{spaces}{line};\n') + + def write_function(self, fp, function_name, info={}, prototype=False): + rtype = info.get('return', 'void') + args = ', '.join([f'{self.name} *drv'] + info.get('args', [])) + fp.write(f'{rtype} {self.name}_{function_name}({args})') + if prototype: + fp.write(';\n') + else: + fp.write('\n{\n') + self.write_scoped_body(fp, info['body']) + fp.write('}\n\n') + + def write_driver(self, fp): + members = io.StringIO() + inits = io.StringIO() + var = 'drv' + for r in self.registers: + members.write(f' volatile {r.name} *r_{r.name};\n') + inits.write(f' {var}->r_{r.name} =\n') + inits.write(f' (volatile {r.name}*)(ptr+{r.name}_OFFSET);\n') + fp.write( + driver_struct_templ.format(driver=self.name, + var=var, + members=members.getvalue().rstrip(), + inits=inits.getvalue().rstrip())) + + impls = [] + fp.write('\n\n// ***** function prototypes ******//\n') + for k, v in self.functions.items(): + if v and 'body' in v: + impls.append((k, v)) + self.write_function(fp, k, v or {}, prototype=True) + fp.write('\n\n// ***** function implementations ******//\n') + for k, v in sorted(impls): + self.write_function(fp, k, v or {}) + + +class ofs_header_writer(object): + def __init__(self, fp): + self.fp = fp + + def close(self): + self.fp.close() + + def write(self, text): + self.fp.write(text) + + def writeline(self, line): + self.write(f'{line}\n') + + @classmethod + @contextmanager + def open(cls, filename, *args, **kwargs): + writer = cls(open(filename, *args, **kwargs)) + try: + yield writer + finally: + writer.close() + + +def declare_fields(pod, fields, indent=0): + fp = io.StringIO() + for f in sorted(fields, key=ofs_field.min, reverse=False): + f.spaces = ' '*indent + f.pod = pod + f.width = max(f.bits)-min(f.bits)+1 + f.to_cpp(fp) + fp.write('\n') + return fp.getvalue().rstrip() + + +def make_headers(args): + data = parse(args.input) + for driver in data.get('drivers', []): + name = driver['name'] + if args.list: + print(f'{name}.h') + elif args.driver is None or args.driver == name: + writer = ofs_driver_writer(driver) + writer.write_header(args.output, args.language) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("input", type=argparse.FileType('r')) + parsers = parser.add_subparsers() + headers_parser = parsers.add_parser('headers') + headers_parser.add_argument('language', choices=['c', 'cpp']) + headers_parser.add_argument('output', + nargs='?', + default=os.getcwd()) + headers_parser.set_defaults(func=make_headers) + headers_parser.add_argument('-l', '--list', action='store_true', + default=False, + help='only list driver names in file') + headers_parser.add_argument('-d', '--driver', + help='process only this driver') + + args = parser.parse_args() + if not hasattr(args, 'func'): + parser.print_usage() + return 1 + args.func(args) + + +if __name__ == '__main__': + main() diff --git a/tests/ofs_driver/CMakeLists.txt b/tests/ofs_driver/CMakeLists.txt new file mode 100644 index 000000000000..a1a903693dd8 --- /dev/null +++ b/tests/ofs_driver/CMakeLists.txt @@ -0,0 +1,32 @@ +## Copyright(c) 2021, Intel Corporation +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and/or other materials provided with the distribution. +## * Neither the name of Intel Corporation nor the names of its contributors +## may be used to endorse or promote products derived from this software +## without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. +ofs_add_driver(ofs_test.yml ofs_test ofs_test.c) + +opae_test_add( + TARGET test_ofs_driver + SOURCE test_ofs_driver.cpp + LIBS ofs_test +) diff --git a/tests/ofs_driver/ofs_test.c b/tests/ofs_driver/ofs_test.c new file mode 100644 index 000000000000..f43dae543cdb --- /dev/null +++ b/tests/ofs_driver/ofs_test.c @@ -0,0 +1,29 @@ +// Copyright(c) 2021, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#include "ofs_test.h" + + + diff --git a/tests/ofs_driver/ofs_test.yml b/tests/ofs_driver/ofs_test.yml new file mode 100644 index 000000000000..19805c154890 --- /dev/null +++ b/tests/ofs_driver/ofs_test.yml @@ -0,0 +1,30 @@ +%TAG !! tag:intel.com,2020: +--- +drivers: + - name: ofs_test + functions: + read_guid: + args: ["uint8_t guid[16]"] + body: | + sz: size_t = 16 + ptr: uint8_t_ptr = ofs_addr(id_lo, uint8_t) + sz + i: size_t + for i in range(sz): + guid[i] = *--ptr + registers: + - !!ofs/register + - [fme_dfh, 0x0000, 0x5010010000000000, "FME DFH"] + - - [feature_type, [63, 60], ro, 0x5, "Feature Type"] + - [dfh_version, [59, 52], ro, 0x1, "DFH Version"] + - [feature_minor_rev, [51, 48], ro, 0x0, "Feature Minor Revision"] + - [reserved41, [47, 41], ro, 0x0, "Reserved"] + - [eol, [40] , ro, 0x0, "End of List"] + - [next_offset, [39, 16], ro, 0x1000, "Next DFH Offset"] + - [feature_major_ver, [15, 12], ro, 0x0, "User defined"] + - [feature_id, [11, 0], ro, 0x0, "Feature ID"] + - !!ofs/register + - [id_lo, 0x0008, 0xB449F9F67228EBF4, "GUID Lower 64 bits"] + - - [bits, [63,0], RO, 0xB449F9F67228EBF4, "Lower 64 bits"] + - !!ofs/register + - [id_hi, 0x0010, 0xB449F9F67228EBF4, "GUID Upper 64 bits"] + - - [bits, [63,0], RO, 0xB449F9F67228EBF4, "Lower 64 bits"] diff --git a/tests/ofs_driver/test_ofs_driver.cpp b/tests/ofs_driver/test_ofs_driver.cpp new file mode 100644 index 000000000000..9ed44f47d8df --- /dev/null +++ b/tests/ofs_driver/test_ofs_driver.cpp @@ -0,0 +1,98 @@ +// Copyright(c) 2021, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#include +#include +#include +#include +#include "gtest/gtest.h" +#include "ofs_test.h" + + +union uuid_bytes { + struct { + uint64_t lo; + uint64_t hi; + }; + uuid_t u; +}; + +int parse_reverse(const char *guid_str, uuid_t reversed) +{ + uuid_t u; + auto r = uuid_parse(guid_str, u); + if (r) return r; + for (int i = 0; i < 16; ++i) { + reversed[i] = u[15-i]; + } + return r; +} + +/** + * @test ofs_test_init + * @brief Tests: ofs_test_init + * @details ofs_test is a sample ofs user mode driver defined in ofs_test.yml + * The build system should generate the ofs_test.h file and use that + * for making an ofs_test library. ofs_test_init is the initializer + * function used to connect data structures in ofs_test to correct + * offsets according to ofs_test.yml. This test calls ofs_test_init + * without a valid fpga_handle so it is expected to return non-zero + * */ +TEST(ofs_driver, ofs_test_init) +{ + ofs_test otest; + fpga_handle h = nullptr; + EXPECT_NE(0, ofs_test_init(&otest, h)); +} + + +/** + * @test ofs_test_read_guid + * @brief Tests: ofs_test_read_guid + * @details The user-mode driver Python code in ofs_test.yml for function + * read_guid reads in the guid registers id_lo and id_hi in reverse + * order. This test gets a uuid string, parses it and reverses it into + * a member of uuid_bytes union. Because we haven't initialized the + * ofs_test data structure, our register variables are invalid. Point + * them to the corresponding data structures in our uuid_bytes + * variable. Call ofs_test_read_guid and use uuid_unparse to get the + * uuid string. This should be equal to the original string we used to + * parse/reverse. + * */ +TEST(ofs_driver, ofs_test_read_guid) +{ + ofs_test otest; + uuid_bytes u; + const char * guid_str = "4bd15660-dec9-4d32-bd3d-e46e87d7d292"; + ASSERT_EQ(parse_reverse(guid_str, u.u), 0); + + otest.r_id_hi = reinterpret_cast(&u.hi); + otest.r_id_lo = reinterpret_cast(&u.lo); + uuid_t u2; + ofs_test_read_guid(&otest, u2); + char unparsed[56]; + uuid_unparse(u2, unparsed); + EXPECT_STREQ(guid_str, unparsed); +}