This commit is contained in:
HailoRT-Automation
2022-03-29 19:08:05 +03:00
commit dd37bf9936
370 changed files with 80735 additions and 0 deletions

212
.logo.svg Normal file
View File

@@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg4986"
version="1.1"
inkscape:version="0.91 r13725"
xml:space="preserve"
width="454.94739"
height="132.55138"
viewBox="0 0 454.94739 132.55138"
sodipodi:docname="Hailo_Logo_RGB.svg"><metadata
id="metadata4992"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4990"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath5014"><path
d="M 0,110 370,110 370,0 0,0 0,110 Z"
id="path5016"
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2048"
inkscape:window-height="1100"
id="namedview4988"
showgrid="false"
inkscape:zoom="2.9109866"
inkscape:cx="216.90465"
inkscape:cy="59.262279"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g4994"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" /><g
id="g4994"
inkscape:groupmode="layer"
inkscape:label="Hailo_Logo_RGB"
transform="matrix(1.25,0,0,-1.25,-5.127625,136.24475)"><g
id="g4996"
transform="translate(172.3149,50.2915)"><path
d="M 0,0 -14.161,0 -40.557,47.98 -40.674,48.181 -40.793,47.98 -67.189,0 -81.35,0 l 34.496,58.684 -0.011,0.02 0.023,0 12.334,0 0.023,0 -0.011,-0.02 L 0,0 Z"
style="fill:#0098c4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4998"
inkscape:connector-curvature="0" /></g><path
d="m 186.287,50.291 14.16,0 0,58.704 -14.16,0 0,-58.704 z"
style="fill:#0098c4;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5000"
inkscape:connector-curvature="0" /><g
id="g5002"
transform="translate(4.2656,50.2915)"><path
d="m 0,0 14.298,0 0,24.747 44.268,0 0,-24.747 14.161,0 0,58.704 -14.161,0 0,-22.272 -44.268,0 0,22.272 L 0,58.704 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5004"
inkscape:connector-curvature="0" /></g><g
id="g5006"
transform="translate(221.0957,50.2915)"><path
d="m 0,0 59.941,0 0,10.999 -45.643,0 0,47.705 L 0,58.704 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5008"
inkscape:connector-curvature="0" /></g><g
id="g5010"><g
id="g5012"
clip-path="url(#clipPath5014)"><g
id="g5018"
transform="translate(368.06,64.1768)"><path
d="m 0,0 0,30.933 c 0,11.274 -3.712,13.886 -15.123,13.886 l -44.956,0 c -11.411,0 -15.123,-2.612 -15.123,-13.886 l 0,-30.933 c 0,-11.136 3.575,-13.885 15.123,-13.885 l 44.956,0 C -3.712,-13.885 0,-11.136 0,0 m -14.16,-2.612 -46.881,0 0,36.569 46.881,0 0,-36.569 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5020"
inkscape:connector-curvature="0" /></g><g
id="g5022"
transform="translate(15.4511,10.2974)"><path
d="m 0,0 0.106,-1.816 c -1.637,-0.106 -4.272,-0.16 -7.903,-0.16 -1.086,0 -1.945,0.289 -2.577,0.868 -0.632,0.578 -0.957,1.357 -0.975,2.337 l 0,12.068 c 0.018,0.979 0.343,1.758 0.975,2.337 0.632,0.578 1.491,0.867 2.577,0.867 3.631,0 6.266,-0.053 7.903,-0.16 L 0,14.499 l -7.396,0 c -1.069,0 -1.603,-0.587 -1.603,-1.762 l 0,-4.006 7.957,0 0,-1.896 -7.957,0 0,-5.046 C -8.999,0.596 -8.465,0 -7.396,0 L 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5024"
inkscape:connector-curvature="0" /></g><g
id="g5026"
transform="translate(23.7817,21.3521)"><path
d="m 0,0 0.134,-2.056 c 1.833,1.584 3.604,2.376 5.313,2.376 1.816,0 2.911,-0.765 3.284,-2.296 1.763,1.531 3.507,2.296 5.234,2.296 1.121,0 1.98,-0.303 2.577,-0.907 0.596,-0.606 0.895,-1.496 0.895,-2.671 l 0,-9.746 -2.244,0 0,9.106 c -0.018,0.8 -0.182,1.383 -0.494,1.748 -0.311,0.365 -0.823,0.547 -1.535,0.547 -0.623,0 -1.224,-0.142 -1.802,-0.426 -0.579,-0.285 -1.402,-0.82 -2.47,-1.603 l 0,-9.372 -2.19,0 0,9.106 c 0,0.818 -0.164,1.405 -0.494,1.762 C 5.879,-1.78 5.367,-1.603 4.673,-1.603 4.05,-1.603 3.449,-1.745 2.871,-2.029 2.292,-2.314 1.469,-2.84 0.401,-3.605 l 0,-9.399 -2.27,0 L -1.869,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5028"
inkscape:connector-curvature="0" /></g><g
id="g5030"
transform="translate(50.1899,17.6938)"><path
d="m 0,0 0,-6.889 c 1.815,-0.534 3.204,-0.801 4.166,-0.801 1.174,0 1.993,0.329 2.456,0.988 0.462,0.658 0.694,1.922 0.694,3.791 0,1.816 -0.218,3.089 -0.654,3.819 C 6.226,1.637 5.482,2.002 4.432,2.002 3.755,2.002 3.084,1.847 2.417,1.535 1.749,1.224 0.943,0.711 0,0 m -0.401,3.658 0.187,-2.083 c 0.694,0.748 1.531,1.335 2.51,1.762 0.979,0.428 1.94,0.642 2.884,0.642 1.549,0 2.679,-0.57 3.391,-1.709 0.712,-1.14 1.068,-2.867 1.068,-5.181 0,-2.456 -0.391,-4.196 -1.175,-5.22 -0.783,-1.024 -2.029,-1.535 -3.738,-1.535 -1.709,0 -3.311,0.445 -4.806,1.335 C -0.027,-8.937 0,-9.809 0,-10.948 l 0,-3.765 -2.27,0 0,18.371 1.869,0 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5032"
inkscape:connector-curvature="0" /></g><g
id="g5034"
transform="translate(74.581,18.7085)"><path
d="m 0,0 c -0.543,0.694 -1.526,1.041 -2.95,1.041 -1.425,0 -2.404,-0.347 -2.938,-1.041 -0.534,-0.694 -0.8,-1.985 -0.8,-3.872 0,-1.887 0.266,-3.177 0.8,-3.872 0.534,-0.694 1.513,-1.041 2.938,-1.041 1.424,0 2.407,0.347 2.95,1.041 0.543,0.695 0.814,1.985 0.814,3.872 C 0.814,-1.985 0.543,-0.694 0,0 m -2.95,2.964 c 2.242,0 3.822,-0.517 4.739,-1.549 0.917,-1.033 1.375,-2.795 1.375,-5.287 0,-2.492 -0.458,-4.254 -1.375,-5.287 -0.917,-1.032 -2.497,-1.549 -4.739,-1.549 -2.226,0 -3.801,0.517 -4.727,1.549 -0.926,1.033 -1.388,2.795 -1.388,5.287 0,2.492 0.462,4.254 1.388,5.287 0.926,1.032 2.501,1.549 4.727,1.549"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5036"
inkscape:connector-curvature="0" /></g><g
id="g5038"
transform="translate(103.165,21.3521)"><path
d="m 0,0 -3.792,-12.363 c -0.106,-0.428 -0.4,-0.641 -0.881,-0.641 l -2.056,0 c -0.213,0 -0.405,0.067 -0.574,0.2 -0.169,0.134 -0.271,0.307 -0.307,0.521 l -2.136,8.838 c -0.053,0.249 -0.125,0.601 -0.213,1.055 -0.09,0.454 -0.161,0.787 -0.214,1.001 l -0.321,0 -0.454,-2.056 -2.109,-8.838 c -0.125,-0.481 -0.427,-0.721 -0.908,-0.721 l -2.029,0 c -0.481,0 -0.775,0.213 -0.881,0.641 L -20.667,0 l 2.35,0 2.697,-9.586 c 0.16,-0.552 0.302,-1.202 0.427,-1.949 l 0.347,0 0.481,1.949 2.162,8.865 c 0.089,0.48 0.374,0.721 0.855,0.721 l 2.056,0 c 0.445,0 0.729,-0.25 0.854,-0.748 l 2.137,-8.838 c 0.035,-0.196 0.106,-0.512 0.213,-0.948 0.107,-0.436 0.187,-0.77 0.24,-1.001 l 0.348,0 c 0.017,0.089 0.075,0.369 0.173,0.841 0.098,0.471 0.191,0.841 0.281,1.108 L -2.35,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5040"
inkscape:connector-curvature="0" /></g><g
id="g5042"
transform="translate(110.2675,15.5044)"><path
d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5044"
inkscape:connector-curvature="0" /></g><g
id="g5046"
transform="translate(133.9511,21.6724)"><path
d="m 0,0 -0.267,-2.136 -0.748,0 c -0.658,0 -1.339,-0.13 -2.042,-0.387 -0.704,-0.259 -1.642,-0.681 -2.817,-1.269 l 0,-9.532 -2.27,0 0,13.004 1.763,0 0.24,-2.056 C -4.13,-0.792 -2.279,0 -0.587,0 L 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5048"
inkscape:connector-curvature="0" /></g><path
d="m 141.801,8.348 -2.243,0 0,13.004 2.243,0 0,-13.004 z m -1.656,18.531 1.069,0 c 0.462,0 0.694,-0.232 0.694,-0.694 l 0,-1.522 c 0,-0.463 -0.232,-0.694 -0.694,-0.694 l -1.069,0 c -0.462,0 -0.694,0.231 -0.694,0.694 l 0,1.522 c 0,0.462 0.232,0.694 0.694,0.694"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5050"
inkscape:connector-curvature="0" /><g
id="g5052"
transform="translate(150.6127,21.3521)"><path
d="m 0,0 0.16,-2.056 c 2.1,1.584 4.041,2.376 5.821,2.376 2.35,0 3.525,-1.193 3.525,-3.578 l 0,-9.746 -2.27,0 0,9.106 c 0,0.854 -0.143,1.45 -0.427,1.788 -0.285,0.339 -0.766,0.507 -1.442,0.507 -0.712,0 -1.429,-0.16 -2.15,-0.48 -0.72,-0.32 -1.677,-0.846 -2.87,-1.575 l 0,-9.346 -2.27,0 L -1.923,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5054"
inkscape:connector-curvature="0" /></g><g
id="g5056"
transform="translate(169.25,15.6777)"><path
d="m 0,0 c 0.516,-0.365 1.415,-0.547 2.697,-0.547 1.282,0 2.18,0.182 2.697,0.547 0.516,0.365 0.774,0.983 0.774,1.856 0,0.872 -0.254,1.491 -0.761,1.855 C 4.899,4.076 3.996,4.259 2.697,4.259 1.415,4.259 0.516,4.076 0,3.711 -0.517,3.347 -0.774,2.728 -0.774,1.856 -0.774,0.983 -0.517,0.365 0,0 M 4.593,-6.715 0.16,-6.101 c -0.908,-0.694 -1.362,-1.523 -1.362,-2.483 0,-0.908 0.267,-1.504 0.801,-1.79 0.534,-0.284 1.611,-0.427 3.231,-0.427 1.585,0 2.648,0.147 3.191,0.441 0.543,0.294 0.814,0.885 0.814,1.776 0,0.64 -0.138,1.077 -0.413,1.308 -0.276,0.231 -0.886,0.418 -1.829,0.561 M 9.666,4.419 7.743,4.259 C 8.099,3.671 8.277,2.871 8.277,1.856 8.277,0.396 7.859,-0.659 7.022,-1.309 6.186,-1.958 4.744,-2.283 2.697,-2.283 1.7,-2.283 0.801,-2.194 0,-2.016 c -0.338,-0.41 -0.437,-0.859 -0.294,-1.348 0.143,-0.49 0.543,-0.788 1.202,-0.895 l 4.86,-0.774 c 1.21,-0.178 2.06,-0.557 2.549,-1.135 0.49,-0.579 0.735,-1.419 0.735,-2.523 0,-1.513 -0.45,-2.564 -1.349,-3.151 -0.899,-0.588 -2.514,-0.881 -4.846,-0.881 -2.35,0 -3.983,0.289 -4.9,0.868 -0.917,0.578 -1.375,1.606 -1.375,3.084 0,0.765 0.16,1.379 0.48,1.842 0.321,0.463 0.864,0.899 1.629,1.308 -0.658,0.517 -0.961,1.202 -0.907,2.057 0.053,0.854 0.391,1.522 1.014,2.002 -1.104,0.587 -1.655,1.718 -1.655,3.391 0,1.495 0.414,2.564 1.241,3.204 0.828,0.641 2.274,0.962 4.34,0.962 1.228,0 2.233,-0.107 3.017,-0.321 l 4.058,0 -0.133,-1.255 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5058"
inkscape:connector-curvature="0" /></g><path
d="m 201.456,8.348 -2.35,0 0,18.424 2.35,0 0,-18.424 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5060"
inkscape:connector-curvature="0" /><g
id="g5062"
transform="translate(210.4013,21.3521)"><path
d="m 0,0 0.16,-2.056 c 2.1,1.584 4.041,2.376 5.821,2.376 2.35,0 3.525,-1.193 3.525,-3.578 l 0,-9.746 -2.27,0 0,9.106 c 0,0.854 -0.143,1.45 -0.427,1.788 -0.285,0.339 -0.766,0.507 -1.442,0.507 -0.712,0 -1.429,-0.16 -2.15,-0.48 -0.72,-0.32 -1.677,-0.846 -2.87,-1.575 l 0,-9.346 -2.27,0 L -1.923,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5064"
inkscape:connector-curvature="0" /></g><g
id="g5066"
transform="translate(230.08,19.563)"><path
d="M 0,0 0,-7.637 C 0,-8.26 0.133,-8.705 0.4,-8.972 0.667,-9.239 1.122,-9.373 1.762,-9.373 l 1.976,0 0.267,-1.735 c -0.819,-0.231 -1.798,-0.347 -2.937,-0.347 -1.086,0 -1.914,0.302 -2.483,0.908 -0.57,0.605 -0.855,1.477 -0.855,2.616 l 0,7.931 -2.35,0 0,1.655 2.35,0.107 0,3.872 2.27,0 0,-3.845 4.139,0 L 4.139,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5068"
inkscape:connector-curvature="0" /></g><g
id="g5070"
transform="translate(241.455,15.5044)"><path
d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5072"
inkscape:connector-curvature="0" /></g><g
id="g5074"
transform="translate(259.0776,27.0391)"><path
d="m 0,0 0,-15.113 c -0.018,-0.57 0.125,-1.001 0.427,-1.295 0.303,-0.294 0.748,-0.441 1.336,-0.441 l 1.255,0 0.267,-1.735 c -0.481,-0.231 -1.273,-0.347 -2.377,-0.347 -0.979,0 -1.754,0.289 -2.323,0.868 -0.57,0.578 -0.855,1.392 -0.855,2.443 L -2.27,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5076"
inkscape:connector-curvature="0" /></g><g
id="g5078"
transform="translate(269.7314,27.0391)"><path
d="m 0,0 0,-15.113 c -0.018,-0.57 0.125,-1.001 0.427,-1.295 0.303,-0.294 0.748,-0.441 1.336,-0.441 l 1.255,0 0.267,-1.735 c -0.481,-0.231 -1.273,-0.347 -2.377,-0.347 -0.979,0 -1.754,0.289 -2.323,0.868 -0.57,0.578 -0.855,1.392 -0.855,2.443 L -2.27,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5080"
inkscape:connector-curvature="0" /></g><path
d="m 280.546,8.348 -2.243,0 0,13.004 2.243,0 0,-13.004 z m -1.656,18.531 1.068,0 c 0.463,0 0.694,-0.232 0.694,-0.694 l 0,-1.522 c 0,-0.463 -0.231,-0.694 -0.694,-0.694 l -1.068,0 c -0.463,0 -0.694,0.231 -0.694,0.694 l 0,1.522 c 0,0.462 0.231,0.694 0.694,0.694"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5082"
inkscape:connector-curvature="0" /><g
id="g5084"
transform="translate(289.8642,15.6777)"><path
d="m 0,0 c 0.516,-0.365 1.415,-0.547 2.697,-0.547 1.282,0 2.18,0.182 2.697,0.547 0.516,0.365 0.774,0.983 0.774,1.856 0,0.872 -0.254,1.491 -0.761,1.855 C 4.899,4.076 3.996,4.259 2.697,4.259 1.415,4.259 0.516,4.076 0,3.711 -0.517,3.347 -0.774,2.728 -0.774,1.856 -0.774,0.983 -0.517,0.365 0,0 M 4.593,-6.715 0.16,-6.101 c -0.908,-0.694 -1.362,-1.523 -1.362,-2.483 0,-0.908 0.267,-1.504 0.801,-1.79 0.534,-0.284 1.611,-0.427 3.231,-0.427 1.585,0 2.648,0.147 3.191,0.441 0.543,0.294 0.814,0.885 0.814,1.776 0,0.64 -0.138,1.077 -0.413,1.308 -0.276,0.231 -0.886,0.418 -1.829,0.561 M 9.666,4.419 7.743,4.259 C 8.099,3.671 8.277,2.871 8.277,1.856 8.277,0.396 7.859,-0.659 7.022,-1.309 6.186,-1.958 4.744,-2.283 2.697,-2.283 1.7,-2.283 0.801,-2.194 0,-2.016 c -0.338,-0.41 -0.437,-0.859 -0.294,-1.348 0.143,-0.49 0.543,-0.788 1.202,-0.895 l 4.86,-0.774 c 1.21,-0.178 2.06,-0.557 2.549,-1.135 0.49,-0.579 0.735,-1.419 0.735,-2.523 0,-1.513 -0.45,-2.564 -1.349,-3.151 -0.899,-0.588 -2.514,-0.881 -4.846,-0.881 -2.35,0 -3.983,0.289 -4.9,0.868 -0.917,0.578 -1.375,1.606 -1.375,3.084 0,0.765 0.16,1.379 0.48,1.842 0.321,0.463 0.864,0.899 1.629,1.308 -0.658,0.517 -0.961,1.202 -0.907,2.057 0.053,0.854 0.391,1.522 1.014,2.002 -1.104,0.587 -1.655,1.718 -1.655,3.391 0,1.495 0.414,2.564 1.241,3.204 0.828,0.641 2.274,0.962 4.34,0.962 1.228,0 2.233,-0.107 3.017,-0.321 l 4.058,0 -0.133,-1.255 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5086"
inkscape:connector-curvature="0" /></g><g
id="g5088"
transform="translate(306.7929,15.5044)"><path
d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5090"
inkscape:connector-curvature="0" /></g><g
id="g5092"
transform="translate(323.935,21.3521)"><path
d="m 0,0 0.16,-2.056 c 2.1,1.584 4.041,2.376 5.821,2.376 2.35,0 3.525,-1.193 3.525,-3.578 l 0,-9.746 -2.27,0 0,9.106 c 0,0.854 -0.143,1.45 -0.427,1.788 -0.285,0.339 -0.766,0.507 -1.442,0.507 -0.712,0 -1.429,-0.16 -2.15,-0.48 -0.72,-0.32 -1.677,-0.846 -2.87,-1.575 l 0,-9.346 -2.27,0 L -1.923,0 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5094"
inkscape:connector-curvature="0" /></g><g
id="g5096"
transform="translate(349.7817,10.2173)"><path
d="m 0,0 0.241,-1.522 c -1.282,-0.445 -2.769,-0.667 -4.46,-0.667 -2.242,0 -3.831,0.511 -4.766,1.535 -0.934,1.023 -1.402,2.79 -1.402,5.3 0,2.51 0.468,4.272 1.402,5.287 0.935,1.015 2.532,1.522 4.793,1.522 1.709,0 3.106,-0.205 4.192,-0.614 L -0.293,9.372 c -1.175,0.107 -2.35,0.16 -3.525,0.16 -1.567,0 -2.653,-0.346 -3.258,-1.041 -0.605,-0.694 -0.907,-1.976 -0.907,-3.845 0,-1.887 0.302,-3.177 0.907,-3.872 0.605,-0.694 1.691,-1.041 3.258,-1.041 1.424,0 2.697,0.089 3.818,0.267"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5098"
inkscape:connector-curvature="0" /></g><g
id="g5100"
transform="translate(357.125,15.5044)"><path
d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path5102"
inkscape:connector-curvature="0" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 20 KiB

102
CMakeLists.txt Normal file
View File

@@ -0,0 +1,102 @@
cmake_minimum_required(VERSION 3.0.0)
option(HAILO_BUILD_PYBIND "Build Python binding" OFF)
option(HAILO_BUILD_PYHAILORT_VENV "Build pyhailort in venv" ON)
option(HAILO_BUILD_EMULATOR "Build hailort for emulator" OFF)
option(HAILO_BUILD_UT "Build Unit Tests" OFF)
option(HAILO_BUILD_GSTREAMER "Compile gstreamer plugins" OFF)
option(HAILO_BUILD_EXAMPLES "Build examples" OFF)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
project(HailoRT)
# Check build type
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Debug")
set(CMAKE_BUILD_TYPE "Debug")
endif()
message(STATUS "Building ${PROJECT_NAME} in ${CMAKE_BUILD_TYPE}")
# Set compiler flags in HAILORT_COMPILE_OPTIONS
# TODO: Change HAILORT_COMPILE_OPTIONS to add_compile_options
if(WIN32)
# TODO: set this eventually? set(HAILORT_COMPILE_OPTIONS /Wall)
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS}
/W4
/WX
/DWIN32_LEAN_AND_MEAN
/DNOMINMAX # NOMINMAX is required in order to play nice with std::min/std::max (otherwise Windows.h defines it's own)
/D_HAILO_EXPORTING
/wd4201 # Anonymous union/struct
/wd4251 # C++ ABI with STL
)
add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Disable "unsafe function" warnings
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} /O2 /DNDEBUG /Zi)
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} /Od /Zi /DDEBUG)
else()
message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
endif()
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/DEBUG>")
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:REF>")
add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:ICF>")
elseif(UNIX)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -Werror -Wall -Wextra -Wconversion)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -Werror -Wall -Wextra
# TODO: remove me warnings
-Wno-conversion
-Wno-deprecated-declarations
-Wno-inconsistent-missing-override
)
else()
message(FATAL_ERROR "Invalid value for CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -O3 -DNDEBUG)
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -O0 -g -DDEBUG)
else()
message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
endif()
else()
message(FATAL_ERROR "Unexpeced host, stopping build")
endif()
enable_testing()
# Flag for emulator (FPGA/Veloce)
if(HAILO_BUILD_EMULATOR)
message(WARNING "HailoRT is building with Emulator flag on")
set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -DHAILO_EMULATOR)
endif()
# Prevent in-tree building
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "In-source builds are not allowed.
Please remove the `CMakeCache.txt` file and `CMakeFiles` directory from `${CMAKE_SOURCE_DIR}`
In order to build, please create a new `build` directory and run `cmake ..` from there.")
endif()
# Enable output of compile commands during generation
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Set validation dir
set(PLATFORM_VALIDATION_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/platform_internals/hailo_platform_internals/validation")
if (NOT DEFINED HEFS_DIR)
set(HEFS_DIR "${PLATFORM_VALIDATION_DIRECTORY}/hefs/latest")
message(STATUS "No HEFS_DIR provided, using default ('${HEFS_DIR}')")
endif()
# Add subdirectories
add_subdirectory(hailort)

43
README.md Normal file
View File

@@ -0,0 +1,43 @@
<p align="left">
<img src=".logo.svg" />
</p>
# HailoRT #
HailoRT is a light-weight and production-grade run-time library, which runs on the host processor, and
implements a robust user-space run-time library (HailoRT Library) responsible for operating a Hailo device, with intuitive APIs in C/C++ for optimized performance.
HailoRT is comprised of the following main components:
- HailoRT Library.
- HailoRT CLI - command line application used to control the Hailo device, run inference using the device,
collect inference statistics and device events, etc.
- [**HailoRT PCIe Driver**](https://github.com/hailo-ai/hailort-drivers) - the device driver used to manage the Hailo device, communicate with the device and transfer
data to/from the device. The PCIe driver includes the Hailo-8 firmware that runs on the Hailo device, manages the boot and control of the Hailo device.
- pyHailoRT - HailoRT Python API (wraps the run-time library)
- HailoRT GStreamer element (HailoNet).
HailoRT supports Linux and Windows, and can be compiled from sources to be integrated with various x86 and ARM processors.
## Usage
See [**hailo.ai developer zone documentation**](https://hailo.ai/developer-zone/documentation/hailort/latest/) (registration is required for full documentation access).
## Changelog
See [**hailo.ai developer zone - HailoRT changelog**](https://hailo.ai/developer-zone/documentation/hailort/latest/?sp_referrer=changelog/changelog.html) (registration required).
## Contact
Contact information and support is available at [**hailo.ai**](https://hailo.ai/contact-us/).
## About Hailo-8™
Hailo-8 is a deep learning processor for edge devices. The Hailo-8 provides groundbraking efficiency for neural network deployment.
The Hailo-8 edge AI processor, featuring up to 26 tera-operations per second (TOPS), significantly outperforms all other edge processors.
Hailo-8 is available in various form-factors, including the Hailo-8 M.2 Module.
The Hailo-8 AI processor is designed to fit into a multitude of smart machines and devices, for a wide variety of sectors including Automotive, Smart Cities, Industry 4.0,
Retail and Smart Homes.
For more information, please visit [**hailo.ai**](https://hailo.ai/).

View File

@@ -0,0 +1,79 @@
{
"_comment":
[
"This file defines the available fields of the firmwares config. It is not used to serialize any data.",
"WARNING! DO NOT CHANGE THE ORDER OF THE DEFINITIONS AS IT WILL CHANGE THEIR GENERATED VALUES!"
],
"version": 0,
"categories":
{
"network":
{
"entries":
{
"should_use_dhcp": {"size": 1, "deserialize_as": "bool"},
"mac_address": {"size": 1, "length": 6, "deserialize_as": "mac_address"},
"static_ip_address": {"size": 4, "deserialize_as": "ipv4"},
"static_gw_address": {"size": 4, "deserialize_as": "ipv4"},
"static_netmask": {"size": 4, "deserialize_as": "ipv4"},
"rx_pause_frames_enable": {"size": 1, "deserialize_as": "bool"}
}
},
"system":
{
"entries":
{
"name": {"size": 1, "length": 32, "deserialize_as": "str"},
"app_watchdog_enable": {"size": 1, "deserialize_as": "bool"},
"app_watchdog_cycles": {"size": 4, "deserialize_as": "int"},
"core_watchdog_enable": {"size": 1, "deserialize_as": "bool"},
"core_watchdog_cycles": {"size": 4, "deserialize_as": "int"},
"watchdog_mode" : {"size": 1, "deserialize_as": "watchdog_mode"},
"max_neural_network_core_clock_rate": {"size": 4, "deserialize_as": "clock_frequency"},
"supported_aspm_states": {"size": 1, "deserialize_as": "supported_aspm_states"},
"bus_0_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
"bus_1_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
"bus_2_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
"bus_3_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
"supported_aspm_l1_substates": {"size": 1, "deserialize_as": "supported_aspm_l1_substates"},
"overcurrent_parameters_source": {"size": 1, "deserialize_as": "overcurrent_parameters_source"},
"overcurrent_monitoring_red_threshold": {"size": 4, "deserialize_as": "int"},
"overcurrent_conversion_time_microseconds": {"size": 4, "deserialize_as": "conversion_time"},
"temperature_parameters_source": {"size": 1, "deserialize_as": "temperature_parameters_source"},
"temperature_red_threshold": {"size": 1, "deserialize_as": "int"},
"temperature_red_hysteresis_threshold": {"size": 1, "deserialize_as": "int"},
"temperature_orange_threshold": {"size": 1, "deserialize_as": "int"},
"temperature_orange_hysteresis_threshold": {"size": 1, "deserialize_as": "int"},
"temperature_throttling_enable": {"size": 1, "deserialize_as": "bool"},
"overcurrent_monitoring_orange_threshold_enable": {"size": 1, "deserialize_as": "bool"}
}
},
"control":
{
"entries":
{
"udp_port": {"size": 2, "deserialize_as": "int"}
}
},
"d2h_event":
{
"entries":
{
"host_udp_port": {"size": 2, "deserialize_as": "int"},
"src_udp_port": {"size": 2, "deserialize_as": "int"},
"host_ip_address": {"size": 4, "deserialize_as": "ipv4"},
"connection_type": {"size": 1, "deserialize_as": "bool"}
}
},
"logger":
{
"entries":
{
"send_via_pci": {"size": 1, "deserialize_as": "bool"},
"send_via_uart": {"size": 1, "deserialize_as": "bool"},
"logger_level": {"size": 4, "deserialize_as": "logger_level"}
}
}
}
}

144
common/config_schema.json Normal file
View File

@@ -0,0 +1,144 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "config_schema",
"description": "schema for user config",
"type": "object",
"properties":
{
"network":
{
"type": "object",
"properties":
{
"should_use_dhcp": {"type": "boolean"},
"mac_address": {"$ref": "#/definitions/mac_address"},
"static_ip_address": {"type": "string", "format": "ipv4"},
"static_gw_address": {"type": "string", "format": "ipv4"},
"static_netmask": {"type": "string", "format": "ipv4"},
"rx_pause_frames_enable": {"type": "boolean"}
}
},
"system":
{
"type": "object",
"properties":
{
"name": {"type": "string", "maxLength": 32},
"app_watchdog_enable": {"type": "boolean"},
"app_watchdog_cycles": {"$ref": "#/definitions/uint16_t"},
"core_watchdog_enable": {"type": "boolean"},
"core_watchdog_cycles": {"$ref": "#/definitions/uint16_t"},
"watchdog_mode" : {"$ref": "#/definitions/watchdog_mode"},
"max_neural_network_core_clock_rate": {"$ref": "#/definitions/clock_frequency"},
"supported_aspm_states": {"$ref": "#/definitions/supported_aspm_states"},
"bus_0_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
"bus_1_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
"bus_2_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
"bus_3_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
"supported_aspm_l1_substates": {"$ref": "#/definitions/supported_aspm_l1_substates"},
"overcurrent_parameters_source": {"$ref": "#/definitions/overcurrent_parameters_source"},
"overcurrent_monitoring_red_threshold": {"$ref": "#/definitions/uint32_t"},
"overcurrent_conversion_time_microseconds": {"$ref": "#/definitions/conversion_time"},
"temperature_parameters_source": {"$ref": "#/definitions/temperature_parameters_source"},
"temperature_red_threshold": {"$ref": "#/definitions/int8_t"},
"temperature_red_hysteresis_threshold": {"$ref": "#/definitions/int8_t"},
"temperature_orange_threshold": {"$ref": "#/definitions/int8_t"},
"temperature_orange_hysteresis_threshold": {"$ref": "#/definitions/int8_t"},
"overcurrent_monitoring_orange_threshold_enable": {"type": "boolean"}
}
},
"control":
{
"type": "object",
"properties":
{
"udp_port": {"$ref": "#/definitions/uint16_t"}
}
},
"d2h_event":
{
"type": "object",
"properties":
{
"host_udp_port": {"$ref": "#/definitions/uint16_t"},
"src_udp_port": {"$ref": "#/definitions/uint16_t"},
"host_ip_address": {"type": "string", "format": "ipv4"},
"connection_type": {"type": "boolean"}
}
},
"logger":
{
"type": "object",
"properties":
{
"send_via_pci": {"type": "boolean"},
"send_via_uart": {"type": "boolean"},
"logger_level": {"$ref": "#/definitions/logger_level"}
}
}
},
"definitions":
{
"mac_address":
{
"type": "string",
"pattern": "^(([0-9a-fA-F]{2}[:]){5}|([0-9a-fA-F]{2}){5})([0-9a-fA-F]{2})$"
},
"clock_frequency":
{
"enum":
["100MHZ", "200MHZ", "400MHZ"]
},
"supported_aspm_states":
{
"enum":
["ASPM_DISABLED", "ASPM_L1_ONLY", "ASPM_L0S_L1",
"ASPM DISABLED", "ASPM L1 ONLY", "ASPM L0S L1"]
},
"i2c_speed":
{
"enum":
["I2C_SPEED_STANDARD", "I2C_SPEED_FAST",
"I2C SPEED STANDARD", "I2C SPEED FAST"]
},
"supported_aspm_l1_substates":
{
"enum":
["ASPM_L1_SUBSTATES_DISABLED", "ASPM_L1_SUBSTATES_L11_ONLY", "ASPM_L1_SUBSTATES_L11_L12",
"ASPM L1 SUBSTATES DISABLED", "ASPM L1 SUBSTATES L1.1 ONLY", "ASPM L1 SUBSTATES L1.1 L1.2"]
},
"watchdog_mode":
{
"enum":
["WD_MODE_HW_SW", "WD_MODE_HW_ONLY",
"WD MODE HW SW", "WD MODE HW ONLY"]
},
"logger_level":
{
"enum":
["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "FATAL"]
},
"overcurrent_parameters_source":
{
"enum":
["FW_VALUES", "USER_CONFIG_VALUES",
"BOARD_CONFIG_VALUES", "OVERCURRENT_DISABLED",
"FW VALUES", "USER CONFIG VALUES",
"BOARD CONFIG VALUES", "OVERCURRENT DISABLED"]
},
"temperature_parameters_source":
{
"enum":
["FW_VALUES", "USER_CONFIG_VALUES",
"FW VALUES", "USER CONFIG VALUES"]
},
"conversion_time":
{
"enum":
[140, 204, 332, 588,
1100, 2116, 4156, 8244]
},
"uint16_t": {"type": "integer", "minimum": 0, "maximum": 65535},
"uint32_t": {"type": "integer", "minimum": 0, "maximum": 4294967295}
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file byte_order.h
* @brief Defines byte order operations.
**/
#ifndef __BYTE_ORDER_H__
#define __BYTE_ORDER_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#if !defined(__BYTE_ORDER__)
// TODO: Check this better?
#if defined(_MSC_VER)
#define __ORDER_LITTLE_ENDIAN__ (1)
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#else
#error "Unexpected byte order"
#endif
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define BYTE_ORDER__htonl(x) (x)
#define BYTE_ORDER__ntohs(x) (x)
#define BYTE_ORDER__ntohl(x) (x)
#define BYTE_ORDER__htons(x) (x)
#define BYTE_ORDER__ntohll(x) (x)
#define BYTE_ORDER__htonll(x) (x)
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define BYTE_ORDER__htons(n) ((uint16_t)((uint16_t)((((uint16_t)(n) & 0xFF)) << 8) | (((uint16_t)(n) & 0xFF00) >> 8)))
#define BYTE_ORDER__ntohs(n) ((uint16_t)((uint16_t)((((uint16_t)(n) & 0xFF)) << 8) | (((uint16_t)(n) & 0xFF00) >> 8)))
#define BYTE_ORDER__htonl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
((((uint32_t)(n) & 0xFF00)) << 8) | \
((((uint32_t)(n) & 0xFF0000)) >> 8) | \
((((uint32_t)(n) & 0xFF000000)) >> 24))
#define BYTE_ORDER__ntohl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
((((uint32_t)(n) & 0xFF00)) << 8) | \
((((uint32_t)(n) & 0xFF0000)) >> 8) | \
((((uint32_t)(n) & 0xFF000000)) >> 24))
#define BYTE_ORDER__htonll(n) (((uint64_t) BYTE_ORDER__htonl((n) & 0xFFFFFFFF) << 32) | \
(uint64_t) BYTE_ORDER__htonl((n) >> 32))
#define BYTE_ORDER__ntohll(n) (((uint64_t) BYTE_ORDER__htonl((n) & 0xFFFFFFFF) << 32) | \
(uint64_t) BYTE_ORDER__htonl((n) >> 32))
#endif
#define BYTE_ORDER__switch_endiannessl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
((((uint32_t)(n) & 0xFF00)) << 8) | \
((((uint32_t)(n) & 0xFF0000)) >> 8) | \
((((uint32_t)(n) & 0xFF000000)) >> 24))
#ifdef __cplusplus
}
#endif
#endif /* __BYTE_ORDER_H__ */

View File

@@ -0,0 +1,300 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file context_switch_defs.h
* @brief Declerations of all context switch related structs.
**/
#ifndef __CONTEXT_SWITCH_DEFS__
#define __CONTEXT_SWITCH_DEFS__
#ifdef __cplusplus
extern "C" {
#endif
#include "control_protocol.h"
/**********************************************************************
* Defines
**********************************************************************/
#define CONTEXT_SWITCH_DEFS__TIMESTAMP_INIT_VALUE (0xFFFFFFFF)
#define CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_ADDRESS (1)
#define CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_COUNT (2)
#define CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_BATCH_SIZE (1)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_SHIFT (0)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_WIDTH (4)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_MASK (0x0f)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(src) \
(((uint8_t)(src) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_MASK) >> CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_SHIFT)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_SHIFT (CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_WIDTH)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_MASK (0x70)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(src) \
(((uint8_t)(src) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_MASK) >> CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_SHIFT)
#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_SET(dst, cluster_index, lcu_index) \
(dst) = (((lcu_index) << CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_SHIFT) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_MASK) | \
(((cluster_index) << CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_SHIFT) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_MASK)
#pragma pack(push, 1)
typedef struct {
uint16_t core_bytes_per_buffer;
uint16_t core_buffers_per_frame;
uint16_t periph_bytes_per_buffer;
uint16_t periph_buffers_per_frame;
uint16_t feature_padding_payload;
uint16_t buffer_padding_payload;
uint16_t buffer_padding;
} CONTEXT_SWITCH_DEFS__stream_reg_info_t;
#if defined(_MSC_VER)
typedef enum : uint8_t {
#else
typedef enum __attribute__((packed)) {
#endif
CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS = 0,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_TRIGGER_SEQUENCER,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_DATA_FROM_VDMA_CHANNEL,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_NON_DEFAULT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_DISABLE_LCU,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_INPUT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_OUTPUT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_INPUT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_OUTPUT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_INPUT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_OUTPUT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_VDMA_CHANNEL,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_CHANGE_VDMA_TO_STREAM_MAPPING,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ADD_DDR_PAIR_INFO,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_DDR_BUFFERING_START,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_LCU_INTERRUPT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_SEQUENCER_DONE_INTERRUPT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_INPUT_CHANNEL_TRANSFER_DONE_INTERRUPT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_OUTPUT_CHANNEL_TRANSFER_DONE_INTERRUPT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_MODULE_CONFIG_DONE_INTERRUPT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_APPLICATION_CHANGE_INTERRUPT,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_CFG_CHANNEL,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_DMA_IDLE_ACTION,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_NMS_IDLE,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS,
/* Must be last */
CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT
} CONTEXT_SWITCH_DEFS__ACTION_TYPE_t;
typedef struct {
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type;
uint32_t time_stamp;
} CONTEXT_SWITCH_DEFS__common_action_header_t;
/**
* The layout of a repeated action in the action_list will be as follows:
* 1) CONTEXT_SWITCH_DEFS__common_action_header_t with 'action_type' CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION
* 2) CONTEXT_SWITCH_DEFS__repeated_action_header_t
* 3) 'count' sub-actions whose type matches the 'sub_action_type' defined by (1).
* The sub-actions will be consecutive, and won't have 'CONTEXT_SWITCH_DEFS__common_action_header_t's
*
* E.g - 3 repeated 'CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t's:
* |-------------------------------------------------------------------------------------------------------|
* | action_list | data |
* |-------------------------------------------------------------------------------------------------------|
* | ... | |
* | | | CONTEXT_SWITCH_DEFS__common_action_header_t { |
* | | | .action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION; |
* | | | .time_stamp = <time_of_last_executed_action_in_repeated>; |
* | | | } |
* | | | CONTEXT_SWITCH_DEFS__repeated_action_header_t { |
* | | | .count = 3; |
* | | | .last_executed = <last_action_executed_in_repeated>; |
* | | | .sub_action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT; |
* | | | } |
* | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=<some_lcu_id>; } |
* | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=<some_lcu_id>; } |
* | V | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=<some_lcu_id>; } |
* | ... | (Next action starting with CONTEXT_SWITCH_DEFS__common_action_header_t) |
* |-------------------------------------------------------------------------------------------------------|
* See also: "CONTROL_PROTOCOL__REPEATED_ACTION_t" in "control_protocol.h"
*/
typedef struct {
uint8_t count;
uint8_t last_executed;
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t sub_action_type;
} CONTEXT_SWITCH_DEFS__repeated_action_header_t;
typedef struct {
uint16_t descriptors_count;
uint8_t cfg_channel_number;
} CONTEXT_SWITCH_DEFS__read_vdma_action_data_t;
typedef struct {
uint16_t ccw_bursts;
uint8_t cfg_channel_number;
} CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t;
typedef struct {
uint8_t cluster_index;
CONTORL_PROTOCOL__sequencer_config_t sequencer_config;
} CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t;
typedef struct {
uint8_t packed_lcu_id;
uint16_t kernel_done_address;
uint32_t kernel_done_count;
} CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t;
/* Default action - kernel_done_address and kernel_done_count has default values */
typedef struct {
uint8_t packed_lcu_id;
} CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t;
typedef struct {
uint8_t packed_lcu_id;
} CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t;
typedef struct {
uint8_t vdma_channel_index;
uint8_t edge_layer_direction;
bool is_inter_context;
} CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t;
typedef struct {
uint8_t vdma_channel_index;
uint8_t stream_index;
uint32_t channel_credits;
uint8_t credit_type;
uint16_t periph_bytes_per_buffer;
} CONTEXT_SWITCH_DEFS__fetch_data_action_data_t;
typedef struct {
uint8_t vdma_channel_index;
uint8_t stream_index;
bool is_dummy_stream;
} CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t;
typedef struct {
uint8_t h2d_vdma_channel_index;
uint8_t d2h_vdma_channel_index;
uint32_t descriptors_per_batch;
uint16_t programmed_descriptors_count;
} CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t;
/* wait for interrupt structs */
typedef struct {
uint8_t packed_lcu_id;
} CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t;
typedef struct {
uint8_t vdma_channel_index;
} CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t;
typedef struct {
uint8_t sequencer_index;
} CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t;
typedef struct {
uint8_t aggregator_index;
uint8_t pred_cluster_ob_index;
uint8_t pred_cluster_ob_cluster_index;
uint8_t pred_cluster_ob_interface;
uint8_t succ_prepost_ob_index;
uint8_t succ_prepost_ob_interface;
} CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t;
typedef struct {
uint8_t vdma_channel_index;
uint8_t stream_index;
bool is_inter_context;
} CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t;
typedef struct {
uint8_t module_index;
} CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t;
typedef struct {
uint8_t application_index;
} CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t;
/* edge layers structs */
typedef struct {
uint8_t stream_index;
uint8_t vdma_channel_index;
CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
bool is_single_context_app;
} CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t;
typedef struct {
uint8_t stream_index;
uint8_t vdma_channel_index;
CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
uint64_t host_descriptors_base_address;
uint16_t initial_host_available_descriptors;
uint8_t desc_list_depth;
} CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t;
typedef struct {
uint8_t stream_index;
uint8_t vdma_channel_index;
CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
uint64_t host_descriptors_base_address;
uint16_t initial_host_available_descriptors;
uint8_t desc_list_depth;
bool fw_managed_channel;
} CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t;
typedef struct {
uint8_t stream_index;
uint8_t vdma_channel_index;
CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
uint32_t frame_credits_in_bytes;
uint16_t desc_page_size;
} CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t;
typedef struct {
uint8_t stream_index;
uint8_t vdma_channel_index;
CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
// TODO: add this to CONTEXT_SWITCH_DEFS__stream_reg_info_t
uint32_t frame_credits_in_bytes;
uint64_t host_descriptors_base_address;
uint16_t initial_host_available_descriptors;
uint16_t desc_page_size;
uint8_t desc_list_depth;
} CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t;
typedef struct {
uint8_t stream_index;
uint8_t vdma_channel_index;
CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
uint32_t frame_credits_in_bytes;
uint64_t host_descriptors_base_address;
uint16_t initial_host_available_descriptors;
uint16_t desc_page_size;
uint8_t desc_list_depth;
bool fw_managed_channel;
} CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t;
typedef struct {
uint8_t channel_index;
uint64_t host_descriptors_base_address;
uint16_t initial_host_available_descriptors;
} CONTEXT_SWITCH_DEFS__activate_cfg_channel_t;
typedef struct {
uint8_t channel_index;
} CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t;
#pragma pack(pop)
#ifdef __cplusplus
}
#endif
#endif /* __CONTEXT_SWITCH_DEFS__ */

File diff suppressed because it is too large Load Diff

167
common/include/d2h_events.h Normal file
View File

@@ -0,0 +1,167 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file d2h_events.h
* @brief Declares all d2h event manager structures relevant in the host.
**/
#ifndef __D2H_EVENTS_H__
#define __D2H_EVENTS_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "status.h"
#include "stdfloat.h"
/**
* @brief The d2h event manager structures relevant in the host
*/
typedef enum {
D2H_EVENT_PRIORITY_INFO = 0,
D2H_EVENT_PRIORITY_CRITICAL,
/* Must be last */
D2H_EVENT_PRIORITY_COUNT
} D2H_EVENT_PRIORITY_t;
typedef enum {
D2H_EVENT_COMMUNICATION_TYPE_UDP = 0,
D2H_EVENT_COMMUNICATION_TYPE_PCIE,
D2H_EVENT_COMMUNICATION_TYPE__COUNT
} D2H_EVENT_COMMUNICATION_TYPE_t;
typedef struct {
uint32_t version;
uint32_t sequence;
uint32_t priority;
uint32_t module_id;
uint32_t event_id;
uint32_t parameter_count;
uint32_t payload_length;
} D2H_EVENT_HEADER_t;
/* D2H_EVENT_ID_t Should be in the same order as the structs in D2H_EVENT__message_parameters_t union, since the host will parse according to this enum */
/* For example ETHERNET_SERVICE_RX_ERROR_EVENT_ID is 0, so D2H_EVENT_rx_error_event_message_t is the first struct in D2H_EVENT__message_parameters_t */
typedef enum {
ETHERNET_SERVICE_RX_ERROR_EVENT_ID = 0,
D2H_HOST_INFO_EVENT_ID,
HEALTH_MONITOR_TEMPERATURE_ALARM_D2H_EVENT_ID,
HEALTH_MONITOR_CLOSED_STREAMS_D2H_EVENT_ID,
HEALTH_MONITOR_OVERCURRENT_PROTECTION_ALERT_EVENT_ID,
HEALTH_MONITOR_LCU_ECC_CORRECTABLE_EVENT_ID,
HEALTH_MONITOR_LCU_ECC_UNCORRECTABLE_EVENT_ID,
HEALTH_MONITOR_CPU_ECC_ERROR_EVENT_ID,
HEALTH_MONITOR_CPU_ECC_FATAL_EVENT_ID,
CONTEXT_SWITCH_BREAKPOINT_REACHED,
HEALTH_MONITOR_CLOCK_CHANGED_EVENT_ID,
D2H_EVENT_ID_COUNT /* Must be last*/
} D2H_EVENT_ID_t;
/* D2H_EVENT_rx_error_event_message_t should be the same as hailo_rx_error_notification_message_t */
typedef struct {
uint32_t error;
uint32_t queue_number;
uint32_t rx_errors_count;
} D2H_EVENT_rx_error_event_message_t;
#define D2H_EVENT_RX_ERROR_EVENT_PARAMETER_COUNT (3)
/* D2H_EVENT_host_info_event_message_t should be the same as hailo_debug_notification_message_t */
typedef struct {
uint32_t connection_status;
uint32_t connection_type;
uint32_t pcie_is_active;
uint32_t host_port;
uint32_t host_ip_addr;
} D2H_EVENT_host_info_event_message_t;
#define D2H_EVENT_HOST_INFO_EVENT_PARAMETER_COUNT (5)
/* D2H_EVENT_health_monitor_closed_streams_event_message_t should be the same as hailo_health_monitor_dataflow_shutdown_notification_message_t */
typedef struct {
uint32_t closed_input_streams;
uint32_t closed_output_streams;
float32_t ts0_temperature;
float32_t ts1_temperature;
} D2H_EVENT_health_monitor_closed_streams_event_message_t;
#define D2H_EVENT_HEALTH_MONITOR_CLOSED_STREAMS_EVENT_PARAMETER_COUNT (4)
/* D2H_EVENT_health_monitor_temperature_alarm_event_message_t should be the same as hailo_health_monitor_temperature_alarm_notification_message_t */
typedef struct {
uint32_t temperature_zone;
uint32_t alarm_ts_id;
float32_t ts0_temperature;
float32_t ts1_temperature;
} D2H_EVENT_health_monitor_temperature_alarm_event_message_t;
#define D2H_EVENT_HEALTH_MONITOR_TEMPERATURE_ALARM_EVENT_PARAMETER_COUNT (4)
/* D2H_EVENT_health_monitor_overcurrent_alert_event_message_t should be the same as hailo_health_monitor_overcurrent_alert_notification_message_t */
typedef struct {
uint32_t overcurrent_zone;
float32_t exceeded_alert_threshold;
float32_t sampled_current_during_alert;
} D2H_EVENT_health_monitor_overcurrent_alert_event_message_t;
#define D2H_EVENT_HEALTH_MONITOR_OVERCURRENT_ALERT_EVENT_PARAMETER_COUNT (2)
/* D2H_EVENT_health_monitor_lcu_ecc_error_event_message_t should be the same as hailo_health_monitor_lcu_ecc_error_notification_message_t */
typedef struct {
uint16_t cluster_bitmap;
} D2H_EVENT_health_monitor_lcu_ecc_error_event_message_t;
/* D2H_EVENT_health_monitor_cpu_ecc_event_message_t should be the same as hailo_health_monitor_cpu_ecc_error_notification_message_t */
#define D2H_EVENT_HEALTH_MONITOR_LCU_ECC_ERROR_EVENT_PARAMETER_COUNT (1)
typedef struct {
uint32_t memory_bitmap;
} D2H_EVENT_health_monitor_cpu_ecc_event_message_t;
#define D2H_EVENT_HEALTH_MONITOR_CPU_ECC_EVENT_PARAMETER_COUNT (1)
/* D2H_EVENT_context_switch_breakpoint_reached_event_massage_t should be the same as
* CONTROL_PROTOCOL__context_switch_breakpoint_data_t and hailo_context_switch_breakpoint_reached_notification_message_t */
typedef struct {
uint8_t application_index;
uint16_t batch_index;
uint8_t context_index;
uint16_t action_index;
} D2H_EVENT_context_switch_breakpoint_reached_event_massage_t;
#define D2H_EVENT_CONTEXT_SWITCH_BREAKPOINT_REACHED_EVENT_PARAMETER_COUNT (4)
typedef struct {
uint32_t previous_clock;
uint32_t current_clock;
} D2H_EVENT_health_monitor_clock_changed_event_message_t;
#define D2H_EVENT_HEALTH_MONITOR_CLOCK_CHANGED_EVENT_PARAMETER_COUNT (2)
/* D2H_EVENT__message_parameters_t should be in the same order as hailo_notification_message_parameters_t */
typedef union {
D2H_EVENT_rx_error_event_message_t rx_error_event;
D2H_EVENT_host_info_event_message_t host_info_event;
D2H_EVENT_health_monitor_closed_streams_event_message_t health_monitor_closed_streams_event;
D2H_EVENT_health_monitor_temperature_alarm_event_message_t health_monitor_temperature_alarm_event;
D2H_EVENT_health_monitor_overcurrent_alert_event_message_t health_monitor_overcurrent_alert_event;
D2H_EVENT_health_monitor_lcu_ecc_error_event_message_t health_monitor_lcu_ecc_error_event;
D2H_EVENT_health_monitor_cpu_ecc_event_message_t health_monitor_cpu_ecc_event;
D2H_EVENT_context_switch_breakpoint_reached_event_massage_t context_switch_breakpoint_reached_event;
D2H_EVENT_health_monitor_clock_changed_event_message_t health_monitor_clock_changed_event;
} D2H_EVENT__message_parameters_t;
typedef struct {
D2H_EVENT_HEADER_t header;
D2H_EVENT__message_parameters_t message_parameters;
} D2H_EVENT_MESSAGE_t;
#define PCIE_D2H_EVENT_MAX_SIZE (0x370)
/**********************************************************************
* Public Functions
**********************************************************************/
HAILO_COMMON_STATUS_t D2H_EVENTS__parse_event(D2H_EVENT_MESSAGE_t *d2h_event_message);
#ifdef __cplusplus
}
#endif
#endif /* __D2H_EVENTS_H__ */

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file firmware_header.h
* @brief Contains definitions needed for generating and handling a firmware header.
**/
#ifndef __FIRMWARE_HEADER__
#define __FIRMWARE_HEADER__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "utils.h"
#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0)
#define FIRMWARE_HEADER_MAGIC_MERCURY (0xE905DAAB)
typedef enum {
FIRMWARE_HEADER_VERSION_INITIAL = 0,
/* MUST BE LAST */
FIRMWARE_HEADER_VERSION_COUNT
} firmware_header_version_t;
typedef enum {
FIRMWARE_TYPE_HAILO8 = 0,
FIRMWARE_TYPE_MERCURY
} firmware_type_t;
#ifdef MERCURY
#define COMPILED_FIRMWARE_TYPE (FIRMWARE_TYPE_MERCURY)
#elif defined(HAILO8_B0)
#define COMPILED_FIRMWARE_TYPE (FIRMWARE_TYPE_HAILO8)
#endif /* MERCURY */
typedef struct {
uint32_t magic;
uint32_t header_version;
uint32_t firmware_major;
uint32_t firmware_minor;
uint32_t firmware_revision;
uint32_t code_size;
} firmware_header_t;
#if defined(_MSC_VER)
// TODO: warning C4200
#pragma warning(push)
#pragma warning(disable: 4200)
#endif
typedef struct {
uint32_t key_size;
uint32_t content_size;
uint8_t certificates_data[0];
} secure_boot_certificate_t;
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#define MINIMUM_FIRMWARE_CODE_SIZE (20*4)
// Tightly coupled with ld script
#define MAXIMUM_APP_FIRMWARE_CODE_SIZE (0x40000)
#define MAXIMUM_CORE_FIRMWARE_CODE_SIZE (0x18000)
#define MAXIMUM_SECOND_STAGE_CODE_SIZE (0x80000)
#define MAXIMUM_FIRMWARE_CERT_KEY_SIZE (0x1000)
#define MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE (0x1000)
typedef enum {
FW_BINARY_TYPE_INVALID = 0,
FW_BINARY_TYPE_APP_FIRMWARE,
FW_BINARY_TYPE_CORE_FIRMWARE,
FW_BINARY_TYPE_SECOND_STAGE_BOOT
} FW_BINARY_TYPE_t;
#ifdef __cplusplus
}
#endif
#endif /* __FIRMWARE_HEADER__ */

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file firmware_header_utils.h
* @brief Utilities for working with the firmware header.
**/
#ifndef __FIRMWARE_HEADER_UTILS__
#define __FIRMWARE_HEADER_UTILS__
#include <stdint.h>
#include <stdbool.h>
#include "status.h"
#include "firmware_header.h"
#include "firmware_version.h"
#ifdef __cplusplus
extern "C" {
#endif
#define REVISION_NUMBER_SHIFT (0)
#define REVISION_APP_CORE_FLAG_BIT_SHIFT (27)
#define REVISION_RESERVED_0_FLAG_BIT_SHIFT (28)
#define REVISION_RESERVED_1_FLAG_BIT_SHIFT (29)
#define REVISION_DEV_FLAG_BIT_SHIFT (30)
#define REVISION_SECOND_STAGE_FLAG_BIT_SHIFT (31)
#define REVISION_NUMBER_WIDTH (27U)
#define REVISION_APP_CORE_FLAG_BIT_WIDTH (1U)
#define REVISION_RESERVED_0_FLAG_BIT_WIDTH (1U)
#define REVISION_RESERVED_1_FLAG_BIT_WIDTH (1U)
#define REVISION_DEV_FLAG_BIT_WIDTH (1U)
#define REVISION_SECOND_STAGE_FLAG_BIT_WIDTH (1U)
#define REVISION_NUMBER_MASK (GET_MASK(REVISION_NUMBER_WIDTH, REVISION_NUMBER_SHIFT))
#define REVISION_APP_CORE_FLAG_BIT_MASK (GET_MASK(REVISION_APP_CORE_FLAG_BIT_WIDTH, REVISION_APP_CORE_FLAG_BIT_SHIFT))
#define REVISION_RESERVED_0_FLAG_BIT_MASK (GET_MASK(REVISION_RESERVED_0_FLAG_BIT_WIDTH, REVISION_RESERVED_0_FLAG_BIT_SHIFT))
#define REVISION_RESERVED_1_FLAG_BIT_MASK (GET_MASK(REVISION_RESERVED_1_FLAG_BIT_WIDTH, REVISION_RESERVED_1_FLAG_BIT_SHIFT))
#define REVISION_DEV_FLAG_BIT_MASK (GET_MASK(REVISION_DEV_FLAG_BIT_WIDTH, REVISION_DEV_FLAG_BIT_SHIFT))
#define REVISION_SECOND_STAGE_FLAG_BIT_MASK (GET_MASK(REVISION_SECOND_STAGE_FLAG_BIT_WIDTH, REVISION_SECOND_STAGE_FLAG_BIT_SHIFT))
#define GET_REVISION_NUMBER_VALUE(binary_revision) (REVISION_NUMBER_MASK & binary_revision)
#define IS_REVISION_DEV(binary_revision) (REVISION_DEV_FLAG_BIT_MASK == (REVISION_DEV_FLAG_BIT_MASK & binary_revision))
#define DEV_STRING_NOTE(__is_release) ((__is_release)? "" : " (dev)")
/**
* Validates the FW headers.
* this function is used from the firmware, from HailoRT and from the second_stage bootloader to validate the headers.
* @param[in] address Address of the firmware.
* @param[in] firmware_size Size of the firmware.
* If the actual size is unknown, set this to the maximum possible size (for example the size of the Flash
* section holding the FW).
* @param[in] is_firmware_size_unknown Set to true if the firmware size is unknown (for example when loading from Flash).
* @param[out] out_app_firmware_header (optional) App firmware header
* @param[out] out_core_firmware_header (optional) Core firmware header
* @param[out] out_firmware_cert (optional) Firmware certificate header
*/
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_fw_headers(uintptr_t firmware_base_address,
uint32_t firmware_size,
bool is_firmware_size_unknown,
firmware_header_t **out_app_firmware_header,
firmware_header_t **out_core_firmware_header,
secure_boot_certificate_t **out_firmware_cert,
firmware_type_t firmware_type);
/**
* Validates the Second stage headers.
* this function is used from the firmware and from HailoRT to validate the headers.
* @param[in] address Address of the second stage.
* @param[in] second_stage_size Size of the second_stage.
* If the actual size is unknown, set this to the maximum possible size (for example the size of the Flash
* section holding the Second stage).
* @param[out] out_second_stage_header (optional) second_stage header
*/
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_second_stage_headers(uintptr_t second_stage_base_size,
uint32_t second_stage_size,
firmware_header_t **out_second_stage_header,
firmware_type_t firmware_type);
/**
* Returns the binary type (App firmware, Core firmware or Second Stage boot)
* @param[in] binary_revision The binary revision (Third part in binary version)
*/
FW_BINARY_TYPE_t FIRMWARE_HEADER_UTILS__get_fw_binary_type(uint32_t binary_revision);
/**
* Returns true if the new binary version is older then the minumum allowed.
* @param[in] new_binary_version A pointer to the new binary version to update to
* @param[in] minimum_allowed_binary_version A pointer to the minimum binary version that is allowed to be upgraded to
*/
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(const firmware_version_t *new_binary_version,
const firmware_version_t *minimum_allowed_binary_version);
/**
* Validates the binary version to prevent downgrade.
* this function is used from the firmware and from HailoRT to prevent downgrade.
* @param[in] new_binary_version A pointer to the new binary version to update to
* @param[in] minimum_allowed_binary_version A pointer to the minimum binary version that is allowed to be upgraded to
* @param[in] fw_binary_type A bit that indicates which binary type is passed (app firmware, core firmware, second stage boot)
*/
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_binary_version(const firmware_version_t *new_binary_version,
const firmware_version_t *minimum_allowed_binary_version,
FW_BINARY_TYPE_t fw_binary_type);
#ifdef __cplusplus
}
#endif
#endif /* __FIRMWARE_HEADER_UTILS__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file firmware_version.h
* @brief Contains information regarding the firmware version.
**/
#ifndef __FIRMWARE_VERSION__
#define __FIRMWARE_VERSION__
#include <stdint.h>
typedef struct {
uint32_t firmware_major;
uint32_t firmware_minor;
uint32_t firmware_revision;
} firmware_version_t;
#define PACK_FW_VERSION(major, minor, revision) {(major), (minor), (revision)}
#define MINIMUM_SECURED_FW_VERSION (firmware_version_t)PACK_FW_VERSION(2, 6, 0)
const firmware_version_t* FIRMWARE_VERSION__get_version(void);
#endif /* __FIRMWARE_VERSION__ */

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file logger_level.h
* @brief Contains the possible logger level information.
**/
#ifndef __LOGGER_LEVEL__
#define __LOGGER_LEVEL__
typedef enum {
FW_LOGGER_LEVEL_TRACE = 0,
FW_LOGGER_LEVEL_DEBUG,
FW_LOGGER_LEVEL_INFO,
FW_LOGGER_LEVEL_WARN,
FW_LOGGER_LEVEL_ERROR,
FW_LOGGER_LEVEL_FATAL
} FW_LOGGER_LEVEL_t;
#endif //__LOGGER_LEVEL__

49
common/include/md5.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See md5.c for more information.
*/
#ifdef HAVE_OPENSSL
#include <openssl/md5.h>
#elif !defined(_MD5_H)
#define _MD5_H
/** Start of modifications for the open source file. **/
#include "stdint.h"
typedef uint8_t MD5_SUM_t[16];
/* Any 32-bit or wider unsigned integer data type will do */
typedef size_t MD5_u32plus;
/** End of modifications. **/
typedef struct {
MD5_u32plus lo, hi;
MD5_u32plus a, b, c, d;
unsigned char buffer[64];
MD5_u32plus block[16];
} MD5_CTX;
extern void MD5_Init(MD5_CTX *ctx);
extern void MD5_Update(MD5_CTX *ctx, const void *data, size_t size);
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
#endif

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file sensor_config_exports.h
* @brief Sensor config exported defines.
**/
#ifndef __SENSOR_CONFIG_EXPORT__
#define __SENSOR_CONFIG_EXPORT__
#define MAX_CONFIG_NAME_LEN 100
#pragma pack(push, 1)
typedef struct {
uint8_t operation;
uint8_t length; //how many bits of the register
uint8_t page; //If SOC is limited to 8 bit addressing, or some array imager.
uint32_t address;
uint32_t bitmask;
uint32_t value; //8/16/32-bit register value
} SENSOR_CONFIG__operation_cfg_t;
#pragma pack(pop)
typedef struct {
uint32_t sensor_type;
uint32_t config_size;
uint16_t reset_config_size;
uint8_t is_free;
uint8_t no_reset_offset;
uint16_t config_height;
uint16_t config_width;
uint16_t config_fps;
uint16_t section_version;
uint8_t config_name[MAX_CONFIG_NAME_LEN];
} SENSOR_CONFIG__section_info_t;
typedef enum {
SENSOR_CONFIG_OPCODES_WR = 0,
SENSOR_CONFIG_OPCODES_RD,
SENSOR_CONFIG_OPCODES_RMW,
SENSOR_CONFIG_OPCODES_DELAY,
} SENSOR_CONFIG_OPCODES_t;
typedef struct {
uint8_t bus_index;
uint16_t slave_address;
uint8_t register_address_size;
bool should_hold_bus;
uint8_t endianness;
} SENSOR_I2C_SLAVE_INFO_t;
typedef enum {
I2C_SLAVE_ENDIANNESS_BIG_ENDIAN = 0,
I2C_SLAVE_ENDIANNESS_LITTLE_ENDIAN = 1,
I2C_SLAVE_ENDIANNESS_COUNT
} i2c_slave_endianness_t;
#define SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT (8)
#define SENSOR_CONFIG__SENSOR_SECTION_BLOCK_COUNT (7)
#define SENSOR_CONFIG__ISP_SECTIONS_BLOCK_COUNT (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - SENSOR_CONFIG__SENSOR_SECTION_BLOCK_COUNT)
#define SENSOR_CONFIG__ISP_SECTION_INDEX (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - SENSOR_CONFIG__ISP_SECTIONS_BLOCK_COUNT)
#define SENSOR_SECTIONS_INFO_SIZE (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT * sizeof(SENSOR_CONFIG__section_info_t))
#endif /* __SENSOR_CONFIG_EXPORT__ */

61
common/include/status.h Normal file
View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file status.h
* @brief Declares status enum for hailo c platform.
**/
#ifndef __STATUS_H__
#define __STATUS_H__
/**
* @brief The enumeration of all status codes.
*/
typedef enum {
/* global statuses */
HAILO_COMMON_STATUS__SUCCESS = 0,
HAILO_COMMON_STATUS__UNINITIALIZED,
/* Control protocol module errors */
HAILO_STATUS__CONTROL_PROTOCOL__OVERRUN_BEFORE_PARAMETER = 0x1000,
HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED,
HAILO_STATUS__CONTROL_PROTOCOL__OVERRUN_AT_PARAMETER,
HAILO_STATUS__CONTROL_PROTOCOL__UNEXPECTED_ACK_VALUE,
HAILO_STATUS__CONTROL_PROTOCOL__INVALID_VERSION,
HAILO_STATUS__CONTROL_PROTOCOL__PART_OF_THE_MESSAGE_NOT_PARSED,
HAILO_STATUS__CONTROL_PROTOCOL__INVALID_BUFFER_SIZE,
HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__CERT_TOO_LARGE = 0x2000,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_HEADER_SIZE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INCORRECT_FIRMWARE_HEADER_MAGIC,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__UNSUPPORTED_FIRMWARE__HEADER_VERSION,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_SIZE_BELOW_MINIMUM,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_OVERRUNS_RAM_SIZE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_CODE_SIZE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_HEADER_SIZE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_KEY_SIZE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_CONTENT_SIZE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_HEADER,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_CERTIFICATE_HEADER,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CORE_CPU_FIRMWARE_HEADER,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CORE_CPU_FIRMWARE_CERTIFICATE_HEADER,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__LEFTOVER_DATA_AFTER_LAST_FIRMWARE_HEADER,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__DETECTED_PROHIBITED_DOWNGRADE_ATTEMPT,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_BINARY_TYPE,
HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_TYPE,
HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT = 0x3000,
HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH,
HAILO_STATUS__D2H_EVENTS__INVALID_ARGUMENT,
HAILO_STATUS__FIRMWARE_STATUS__NULL_ARGUMENT_PASSED = 0x4000,
HAILO_STATUS__FIRMWARE_STATUS__INVALID_COMPONENT_ID,
HAILO_STATUS__FIRMWARE_STATUS__INVALID_MODULE_ID,
HAILO_STATUS__FIRMWARE_STATUS__INVALID_STATUS_VALUE,
} HAILO_COMMON_STATUS_t;
#endif /* __STATUS_H__ */

16
common/include/stdfloat.h Normal file
View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file stdfloat.h
* @brief Defines fixed-size float types.
**/
#ifndef _STDFLOAT_H
#define _STDFLOAT_H
typedef float float32_t;
typedef double float64_t;
#endif /* _STDFLOAT_H */

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file user_config_common.h
* @brief Contains information regarding the firmware user config.
**/
#ifndef __USER_CONFIG_COMMON__
#define __USER_CONFIG_COMMON__
#include <stdint.h>
#include "logger_level.h"
#define USER_CONFIG_OVERCURRENT_UNINITIALIZED_VALUE (0)
#define USER_CONFIG_TEMPERATURE_DEFAULT_RED_ALARM_THRESHOLD (120.f)
#define USER_CONFIG_TEMPERATURE_DEFAULT_RED_HYSTERESIS_ALARM_THRESHOLD (116.f)
#define USER_CONFIG_TEMPERATURE_DEFAULT_ORANGE_ALARM_THRESHOLD (104.f)
#define USER_CONFIG_TEMPERATURE_DEFAULT_ORANGE_HYSTERESIS_ALARM_THRESHOLD (99.f)
#pragma pack(push, 1)
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4200)
#endif
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t entry_count;
uint8_t entries[0];
} USER_CONFIG_header_t;
typedef struct {
uint16_t category;
uint16_t entry_id;
uint32_t entry_size;
uint8_t value[0];
} USER_CONFIG_ENTRY_t;
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#pragma pack(pop)
// Used by user config defaults
typedef enum {
ASPM_DISABLED = 0,
ASPM_L1_ONLY,
ASPM_L0S_L1
} PCIE_CONFIG_SUPPOPRTED_ASPM_STATES_t;
typedef enum {
ASPM_L1_SUBSTATES_DISABLED = 0,
ASPM_L1_SUBSTATES_L11_ONLY,
ASPM_L1_SUBSTATES_L11_L12
} PCIE_CONFIG_SUPPOPRTED_L1_ASPM_SUBSTATES_t;
typedef enum {
SOC__NN_CLOCK_400MHz = 400 * 1000 * 1000,
SOC__NN_CLOCK_375MHz = 375 * 1000 * 1000,
SOC__NN_CLOCK_350MHz = 350 * 1000 * 1000,
SOC__NN_CLOCK_325MHz = 325 * 1000 * 1000,
SOC__NN_CLOCK_300MHz = 300 * 1000 * 1000,
SOC__NN_CLOCK_275MHz = 275 * 1000 * 1000,
SOC__NN_CLOCK_250MHz = 250 * 1000 * 1000,
SOC__NN_CLOCK_225MHz = 225 * 1000 * 1000,
SOC__NN_CLOCK_200MHz = 200 * 1000 * 1000,
SOC__NN_CLOCK_100MHz = 100 * 1000 * 1000
} SOC__NN_CLOCK_HZ_t;
typedef enum {
WD_SERVICE_MODE_HW_SW = 0,
WD_SERVICE_MODE_HW_ONLY,
WD_SERVICE_NUM_MODES
} WD_SERVICE_wd_mode_t;
typedef enum {
OVERCURRENT_PARAMETERS_SOURCE_FW_VALUES = 0,
OVERCURRENT_PARAMETERS_SOURCE_USER_CONFIG_VALUES,
OVERCURRENT_PARAMETERS_SOURCE_BOARD_CONFIG_VALUES,
OVERCURRENT_PARAMETERS_SOURCE_OVERCURRENT_DISABLED,
} OVERCURRENT_parameters_source_t;
typedef enum {
OVERCURRENT_CONVERSION_PERIOD_140US = 140,
OVERCURRENT_CONVERSION_PERIOD_204US = 204,
OVERCURRENT_CONVERSION_PERIOD_332US = 332,
OVERCURRENT_CONVERSION_PERIOD_588US = 588,
OVERCURRENT_CONVERSION_PERIOD_1100US = 1100,
OVERCURRENT_CONVERSION_PERIOD_2116US = 2116,
OVERCURRENT_CONVERSION_PERIOD_4156US = 4156,
OVERCURRENT_CONVERSION_PERIOD_8244US = 8244
} OVERCURRENT_conversion_time_us_t;
typedef enum {
TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_FW_VALUES = 0,
TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_USER_CONFIG_VALUES
} TEMPERATURE_PROTECTION_parameters_source_t;
#endif /* __USER_CONFIG_COMMON__ */

114
common/include/utils.h Normal file
View File

@@ -0,0 +1,114 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file utils.h
* @brief Defines common utilities.
**/
#ifndef __UTILS_H__
#define __UTILS_H__
/** A compile time assertion check.
*
* Validate at compile time that the predicate is true without
* generating code. This can be used at any point in a source file
* where typedef is legal.
*
* On success, compilation proceeds normally.
*
* On failure, attempts to typedef an array type of negative size. The
* offending line will look like
* typedef assertion_failed_file_h_42[-1]
* where file is the content of the second parameter which should
* typically be related in some obvious way to the containing file
* name, 42 is the line number in the file on which the assertion
* appears, and -1 is the result of a calculation based on the
* predicate failing.
*
* \param predicate The predicate to test. It must evaluate to
* something that can be coerced to a normal C boolean.
*
* \param file A sequence of legal identifier characters that should
* uniquely identify the source file in which this condition appears.
*/
#define CASSERT(predicate, file) \
_impl_CASSERT_LINE(predicate,__LINE__,file) \
#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
ATTR_UNUSED typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
#define ARRAY_LENGTH(__array) (sizeof((__array)) / sizeof((__array)[0]))
#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef DIV_ROUND_UP
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#endif
#ifndef ROUND_UNSIGNED_FLOAT
#define ROUND_UNSIGNED_FLOAT(n) ((n - (uint32_t)(n)) > 0.5) ? (uint32_t)(n + 1) : (uint32_t)(n)
#endif
#ifndef IS_POWEROF2
#define IS_POWEROF2(v) ((v & (v - 1)) == 0)
#endif
#define CPU_CYCLES_NUMBER_IN_MS (configCPU_CLOCK_HZ / 1000)
#define GET_MASK(width, shift) (((1U << (width)) - 1U) << (shift))
/* Argument counter for variadic macros */
#define GET_ARG_COUNT(...) INTERNAL_GET_ARG_COUNT_PRIVATE(0, ## __VA_ARGS__, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define INTERNAL_GET_ARG_COUNT_PRIVATE(_0, _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) count
#ifdef __GNUC__
#define ATTR_UNUSED __attribute__((unused))
#else
#define ATTR_UNUSED
#endif
#define PP_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define PP_RSEQ_N() 6, 5, 4, 3, 2, 1, 0
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_COMMASEQ_N() 1, 1, 1, 1, 1, 0
#define PP_COMMA(...) ,
#define PP_NARG_HELPER3_01(N) 0
#define PP_NARG_HELPER3_00(N) 1
#define PP_NARG_HELPER3_11(N) N
#define PP_NARG_HELPER2(a, b, N) PP_NARG_HELPER3_ ## a ## b(N)
#define PP_NARG_HELPER1(a, b, N) PP_NARG_HELPER2(a, b, N)
#define PP_HASCOMMA(...) \
PP_NARG_(__VA_ARGS__, PP_COMMASEQ_N())
#define PP_NARG(...) \
PP_NARG_HELPER1( \
PP_HASCOMMA(__VA_ARGS__), \
PP_HASCOMMA(PP_COMMA __VA_ARGS__ ()),\
PP_NARG_(__VA_ARGS__, PP_RSEQ_N()))
#define UNUSED0(...)
#define UNUSED1(x) (void)(x)
#define UNUSED2(x,y) (void)(x),(void)(y)
#define UNUSED3(x,y,z) (void)(x),(void)(y),(void)(z)
#define UNUSED4(a,x,y,z) (void)(a),(void)(x),(void)(y),(void)(z)
#define UNUSED5(a,b,x,y,z) (void)(a),(void)(b),(void)(x),(void)(y),(void)(z)
#define UNUSED6(a,b,x,y,z,c) (void)(a),(void)(b),(void)(x),(void)(y),(void)(z),(void)(c)
#define ALL_UNUSED_IMPL_(nargs) UNUSED ## nargs
#define ALL_UNUSED_IMPL(nargs) ALL_UNUSED_IMPL_(nargs)
#define ALL_UNUSED(...) ALL_UNUSED_IMPL( PP_NARG(__VA_ARGS__))(__VA_ARGS__ )
#endif /* __UTILS_H__ */

View File

@@ -0,0 +1,279 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file firmware_header_utils.c
* @brief Utilities for working with the firmware header.
**/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "firmware_header.h"
#include "firmware_version.h"
#include "firmware_header_utils.h"
#include "utils.h"
#include "control_protocol.h"
/* when reading the firmware we don't want to read past the firmware_size,
so we have a consumed_firmware_offset that is updated _before_ accessing data at that offset
of firmware_base_address */
#define CONSUME_FIRMWARE(__size, __status) do { \
consumed_firmware_offset += (uint32_t) (__size); \
if ((firmware_size < (__size)) || (firmware_size < consumed_firmware_offset)) { \
status = __status; \
goto exit; \
} \
} while(0)
static HAILO_COMMON_STATUS_t firmware_header_utils__validate_fw_header(uintptr_t firmware_base_address,
uint32_t firmware_size,
uint32_t max_code_size,
uint32_t *outer_consumed_firmware_offset,
firmware_header_t **out_firmware_header,
firmware_type_t firmware_type)
{
HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
firmware_header_t *firmware_header = NULL;
uint32_t consumed_firmware_offset = *outer_consumed_firmware_offset;
uint32_t firmware_magic = 0;
firmware_header = (firmware_header_t *) (firmware_base_address + consumed_firmware_offset);
CONSUME_FIRMWARE(sizeof(firmware_header_t), HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_HEADER_SIZE);
switch (firmware_type) {
case FIRMWARE_TYPE_HAILO8:
firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO8;
break;
case FIRMWARE_TYPE_MERCURY:
firmware_magic = FIRMWARE_HEADER_MAGIC_MERCURY;
break;
default:
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_TYPE;
goto exit;
}
if (firmware_magic != firmware_header->magic) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INCORRECT_FIRMWARE_HEADER_MAGIC;
goto exit;
}
/* Validate that the firmware header version is supported */
switch(firmware_header->header_version) {
case FIRMWARE_HEADER_VERSION_INITIAL:
break;
default:
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__UNSUPPORTED_FIRMWARE__HEADER_VERSION;
goto exit;
break;
}
if (MINIMUM_FIRMWARE_CODE_SIZE > firmware_header->code_size) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_SIZE_BELOW_MINIMUM;
goto exit;
}
if (max_code_size < firmware_header->code_size) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_OVERRUNS_RAM_SIZE;
goto exit;
}
CONSUME_FIRMWARE(firmware_header->code_size, HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_CODE_SIZE);
*outer_consumed_firmware_offset = consumed_firmware_offset;
*out_firmware_header = firmware_header;
status = HAILO_COMMON_STATUS__SUCCESS;
exit:
return status;
}
static HAILO_COMMON_STATUS_t firmware_header_utils__validate_cert_header(uintptr_t firmware_base_address,
uint32_t firmware_size,
uint32_t *outer_consumed_firmware_offset,
secure_boot_certificate_t **out_firmware_cert)
{
secure_boot_certificate_t *firmware_cert = NULL;
HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
uint32_t consumed_firmware_offset = *outer_consumed_firmware_offset;
firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset);
CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_HEADER_SIZE);
if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) ||
(MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__CERT_TOO_LARGE;
goto exit;
}
CONSUME_FIRMWARE(firmware_cert->key_size, HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_KEY_SIZE);
CONSUME_FIRMWARE(firmware_cert->content_size, HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_CONTENT_SIZE);
*outer_consumed_firmware_offset = consumed_firmware_offset;
*out_firmware_cert = firmware_cert;
status = HAILO_COMMON_STATUS__SUCCESS;
exit:
return status;
}
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_fw_headers(uintptr_t firmware_base_address,
uint32_t firmware_size,
bool is_firmware_size_unknown,
firmware_header_t **out_app_firmware_header,
firmware_header_t **out_core_firmware_header,
secure_boot_certificate_t **out_firmware_cert,
firmware_type_t firmware_type)
{
firmware_header_t *app_firmware_header = NULL;
firmware_header_t *core_firmware_header = NULL;
secure_boot_certificate_t *firmware_cert = NULL;
HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
uint32_t consumed_firmware_offset = 0;
status = firmware_header_utils__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_APP_FIRMWARE_CODE_SIZE,
&consumed_firmware_offset, &app_firmware_header, firmware_type);
if (HAILO_COMMON_STATUS__SUCCESS != status) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_HEADER;
goto exit;
}
status = firmware_header_utils__validate_cert_header(firmware_base_address, firmware_size,
&consumed_firmware_offset, &firmware_cert);
if (HAILO_COMMON_STATUS__SUCCESS != status) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_CERTIFICATE_HEADER;
goto exit;
}
status = firmware_header_utils__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_CORE_FIRMWARE_CODE_SIZE,
&consumed_firmware_offset, &core_firmware_header, firmware_type);
if (HAILO_COMMON_STATUS__SUCCESS != status) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CORE_CPU_FIRMWARE_HEADER;
goto exit;
}
if ((consumed_firmware_offset != firmware_size) && (!is_firmware_size_unknown)) {
/* it is an error if there is leftover data after the last firmware header */
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__LEFTOVER_DATA_AFTER_LAST_FIRMWARE_HEADER;
goto exit;
}
/* the out params are all optional */
if (NULL != out_app_firmware_header) {
*out_app_firmware_header = app_firmware_header;
}
if (NULL != out_firmware_cert) {
*out_firmware_cert = firmware_cert;
}
if (NULL != out_core_firmware_header) {
*out_core_firmware_header = core_firmware_header;
}
status = HAILO_COMMON_STATUS__SUCCESS;
exit:
return status;
}
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_second_stage_headers(uintptr_t second_stage_base_size,
uint32_t second_stage_size,
firmware_header_t **out_second_stage_header,
firmware_type_t firmware_type)
{
firmware_header_t *second_stage_header = NULL;
secure_boot_certificate_t *second_stage_cert = NULL;
HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
uint32_t consumed_second_stage_offset = 0;
status = firmware_header_utils__validate_fw_header(second_stage_base_size, second_stage_size, MAXIMUM_SECOND_STAGE_CODE_SIZE,
&consumed_second_stage_offset, &second_stage_header, firmware_type);
if (HAILO_COMMON_STATUS__SUCCESS != status) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_HEADER;
goto exit;
}
status = firmware_header_utils__validate_cert_header(second_stage_base_size, second_stage_size,
&consumed_second_stage_offset, &second_stage_cert);
if (HAILO_COMMON_STATUS__SUCCESS != status) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_CERTIFICATE_HEADER;
goto exit;
}
if (consumed_second_stage_offset != second_stage_size) {
/* it is an error if there is leftover data after the last firmware header */
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__LEFTOVER_DATA_AFTER_LAST_FIRMWARE_HEADER;
goto exit;
}
/* the out params are all optional */
if (NULL != out_second_stage_header) {
*out_second_stage_header = second_stage_header;
}
status = HAILO_COMMON_STATUS__SUCCESS;
exit:
return status;
}
FW_BINARY_TYPE_t FIRMWARE_HEADER_UTILS__get_fw_binary_type(uint32_t binary_revision)
{
FW_BINARY_TYPE_t fw_binary_type = FW_BINARY_TYPE_INVALID;
// Remove dev flag before checking binary type
binary_revision &= ~(REVISION_DEV_FLAG_BIT_MASK);
if (REVISION_SECOND_STAGE_FLAG_BIT_MASK == (binary_revision & REVISION_SECOND_STAGE_FLAG_BIT_MASK)) {
fw_binary_type = FW_BINARY_TYPE_SECOND_STAGE_BOOT;
} else if (0 == (binary_revision & (REVISION_APP_CORE_FLAG_BIT_MASK))) {
fw_binary_type = FW_BINARY_TYPE_APP_FIRMWARE;
} else if (REVISION_APP_CORE_FLAG_BIT_MASK == (binary_revision & (REVISION_APP_CORE_FLAG_BIT_MASK))) {
fw_binary_type = FW_BINARY_TYPE_CORE_FIRMWARE;
} else {
fw_binary_type = FW_BINARY_TYPE_INVALID;
}
return fw_binary_type;
}
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(const firmware_version_t *new_binary_version,
const firmware_version_t *minimum_allowed_binary_version)
{
bool is_binary_being_downgraded =
// Check if minimum allowed binary's major is greater than new binary's major
(minimum_allowed_binary_version->firmware_major > new_binary_version->firmware_major) ||
// Check if minimum allowed binary's minor is greater than new binary's minor (If major is the same)
((minimum_allowed_binary_version->firmware_major == new_binary_version->firmware_major) &&
(minimum_allowed_binary_version->firmware_minor > new_binary_version->firmware_minor)) ||
// Check if minimum allowed binary's revision is greater than new binary's revision (If major and minor are the same)
((minimum_allowed_binary_version->firmware_major == new_binary_version->firmware_major) &&
(minimum_allowed_binary_version->firmware_minor == new_binary_version->firmware_minor) &&
(GET_REVISION_NUMBER_VALUE(minimum_allowed_binary_version->firmware_revision) >
(GET_REVISION_NUMBER_VALUE(new_binary_version->firmware_revision))));
return is_binary_being_downgraded;
}
HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_binary_version(const firmware_version_t *new_binary_version,
const firmware_version_t *minimum_allowed_binary_version,
FW_BINARY_TYPE_t fw_binary_type)
{
HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
// Make sure downgrade is not executed
if (FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(new_binary_version, minimum_allowed_binary_version)) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__DETECTED_PROHIBITED_DOWNGRADE_ATTEMPT;
goto exit;
}
if (FIRMWARE_HEADER_UTILS__get_fw_binary_type(new_binary_version->firmware_revision) != fw_binary_type) {
status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_BINARY_TYPE;
goto exit;
}
status = HAILO_COMMON_STATUS__SUCCESS;
exit:
return status;
}

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file firmware_status.c
* @brief Defines firmware status codes.
**/
#include <stdint.h>
#include "firmware_status.h"
#include <string.h>
#ifdef FIRMWARE_ARCH
#pragma pack(push, 1)
typedef struct {
const char *status_name;
uint32_t status_id;
} FIRMWARE_STATUS__status_record_t;
typedef struct {
const char *module_name;
uint32_t module_id;
} FIRMWARE_STATUS__module_record_t;
#pragma pack(pop)
#define FIRMWARE_STATUS_SECTION __attribute__((section(".firmware_statuses")))
#define FIRMWARE_MODULE__X(module) static const char module##_str[] FIRMWARE_STATUS_SECTION = #module;
#define FIRMWARE_STATUS__X(name) static const char name##_str[] FIRMWARE_STATUS_SECTION = #name;
FIRMWARE_STATUS__VARIABLES
#undef FIRMWARE_STATUS__X
#undef FIRMWARE_MODULE__X
const FIRMWARE_STATUS__module_record_t FIRMWARE_STATUS__module_records[] FIRMWARE_STATUS_SECTION = {
#define FIRMWARE_MODULE__X(module) { .module_id = module, .module_name = module##_str },
#define FIRMWARE_STATUS__X(name)
FIRMWARE_STATUS__VARIABLES
#undef FIRMWARE_STATUS__X
#undef FIRMWARE_MODULE__X
};
const FIRMWARE_STATUS__status_record_t FIRMWARE_STATUS__status_records[] FIRMWARE_STATUS_SECTION = {
#define FIRMWARE_MODULE__X(module)
#define FIRMWARE_STATUS__X(name) { .status_id = name, .status_name = name##_str },
FIRMWARE_STATUS__VARIABLES
#undef FIRMWARE_STATUS__X
#undef FIRMWARE_MODULE__X
};
#endif
#ifndef FIRMWARE_ARCH
static const char *FIRMWARE_STATUS__textual_format[] =
{
#define FIRMWARE_MODULE__X(module)
#define FIRMWARE_STATUS__X(name) #name,
FIRMWARE_STATUS__VARIABLES
#undef FIRMWARE_STATUS__X
#undef FIRMWARE_MODULE__X
};
/* The FIRMWARE_STATUS__textual_format array stores the strings in "absolute" order.
In order for us to know the absolute index of each status we store an array that for each module stores
the absolute index of it's first status.
This way we can compute the absolute index in O(1) time. */
#define HELPER_INDEX_NAME(__name) __HELPER_FIRMWARE_STATUS__##__name
/* the helper indices counts all module names and statuses.
the goal here is to be able to count how many statuses occured prior to each module.
we mark the module starts with the module__START elements, and in order
to calculate the number of statuses prior to the current module, we take the
module's module__START value, and subtract the number of __START element
of previous modules, which is the enum value of the module. */
typedef enum {
#define FIRMWARE_MODULE__X(module) HELPER_INDEX_NAME(module##__START),
#define FIRMWARE_STATUS__X(name) HELPER_INDEX_NAME(name),
FIRMWARE_STATUS__VARIABLES
#undef FIRMWARE_STATUS__X
#undef FIRMWARE_MODULE__X
} __FIRMWARE_STATUS__helper_indices_t;
static const uint32_t FIRMWARE_STATUS__absolute_module_indices[] = {
#define FIRMWARE_MODULE__X(module) HELPER_INDEX_NAME(module##__START) - module,
#define FIRMWARE_STATUS__X(name)
FIRMWARE_STATUS__VARIABLES
#undef FIRMWARE_STATUS__X
#undef FIRMWARE_MODULE__X
ARRAY_LENGTH(FIRMWARE_STATUS__textual_format)
};
HAILO_COMMON_STATUS_t FIRMWARE_STATUS__get_textual(FIRMWARE_STATUS_t fw_status, const char **text)
{
HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
uint32_t module_id = 0;
uint32_t module_abs_index = 0;
uint32_t next_module_abs_index = 0;
uint32_t status_value = 0;
if (NULL == text) {
status = HAILO_STATUS__FIRMWARE_STATUS__NULL_ARGUMENT_PASSED;
goto exit;
}
if (FIRMWARE_STATUS__COMPONENT_ID != FIRMWARE_STATUS__COMPONENT_GET(fw_status)) {
status = HAILO_STATUS__FIRMWARE_STATUS__INVALID_COMPONENT_ID;
goto exit;
}
module_id = FIRMWARE_STATUS__MODULE_INDEX_GET(fw_status);
if (FIRMWARE_MODULE_COUNT <= module_id) {
status = HAILO_STATUS__FIRMWARE_STATUS__INVALID_MODULE_ID;
goto exit;
}
module_abs_index = FIRMWARE_STATUS__absolute_module_indices[module_id];
next_module_abs_index = FIRMWARE_STATUS__absolute_module_indices[module_id+1];
status_value = (uint32_t)FIRMWARE_STATUS__VALUE_GET(fw_status) - 1; /* status values start at 1 */
/* check status value is in the correct range */
if (status_value >= next_module_abs_index - module_abs_index) {
status = HAILO_STATUS__FIRMWARE_STATUS__INVALID_STATUS_VALUE;
goto exit;
}
*text = FIRMWARE_STATUS__textual_format[module_abs_index + status_value];
status = HAILO_COMMON_STATUS__SUCCESS;
exit:
return status;
}
#endif

291
common/src/md5.c Normal file
View File

@@ -0,0 +1,291 @@
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* (This is a heavily cut-down "BSD license".)
*
* This differs from Colin Plumb's older public domain implementation in that
* no exactly 32-bit integer data type is required (any 32-bit or wider
* unsigned integer data type will do), there's no compile-time endianness
* configuration, and the function prototypes match OpenSSL's. No code from
* Colin Plumb's implementation has been reused; this comment merely compares
* the properties of the two independent implementations.
*
* The primary goals of this implementation are portability and ease of use.
* It is meant to be fast, but not as fast as possible. Some known
* optimizations are not included to reduce source code size and avoid
* compile-time configuration.
*/
#ifndef HAVE_OPENSSL
#include <string.h>
#include "md5.h"
/*
* The basic MD5 functions.
*
* F and G are optimized compared to their RFC 1321 definitions for
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
* implementation.
*/
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define H(x, y, z) (((x) ^ (y)) ^ (z))
#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
/*
* The MD5 transformation for all four rounds.
*/
#define STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
(a) += (b);
/*
* SET reads 4 input bytes in little-endian byte order and stores them in a
* properly aligned word in host byte order.
*
* The check for little-endian architectures that tolerate unaligned memory
* accesses is just an optimization. Nothing will break if it fails to detect
* a suitable architecture.
*
* Unfortunately, this optimization may be a C strict aliasing rules violation
* if the caller's data buffer has effective type that cannot be aliased by
* MD5_u32plus. In practice, this problem may occur if these MD5 routines are
* inlined into a calling function, or with future and dangerously advanced
* link-time optimizations. For the time being, keeping these MD5 routines in
* their own translation unit avoids the problem.
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define SET(n) \
(*(MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
#else
#define SET(n) \
(ctx->block[(n)] = \
(MD5_u32plus)ptr[(n) * 4] | \
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
#define GET(n) \
(ctx->block[(n)])
#endif
/*
* This processes one or more 64-byte data blocks, but does NOT update the bit
* counters. There are no alignment requirements.
*/
static const void *body(MD5_CTX *ctx, const void *data, size_t size)
{
const unsigned char *ptr;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (const unsigned char *)data;
a = ctx->a;
b = ctx->b;
c = ctx->c;
d = ctx->d;
do {
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
/* Round 2 */
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
/* Round 3 */
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
/* Round 4 */
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx->a = a;
ctx->b = b;
ctx->c = c;
ctx->d = d;
return ptr;
}
void MD5_Init(MD5_CTX *ctx)
{
ctx->a = 0x67452301;
ctx->b = 0xefcdab89;
ctx->c = 0x98badcfe;
ctx->d = 0x10325476;
ctx->lo = 0;
ctx->hi = 0;
}
void MD5_Update(MD5_CTX *ctx, const void *data, size_t size)
{
MD5_u32plus saved_lo;
size_t used, available;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
ctx->hi++;
ctx->hi += size >> 29;
used = saved_lo & 0x3f;
if (used) {
available = 64 - used;
if (size < available) {
memcpy(&ctx->buffer[used], data, size);
return;
}
memcpy(&ctx->buffer[used], data, available);
data = (const unsigned char *)data + available;
size -= available;
body(ctx, ctx->buffer, 64);
}
if (size >= 64) {
data = body(ctx, data, size & ~(size_t)0x3f);
size &= 0x3f;
}
memcpy(ctx->buffer, data, size);
}
#define OUT(dst, src) \
(dst)[0] = (unsigned char)(src); \
(dst)[1] = (unsigned char)((src) >> 8); \
(dst)[2] = (unsigned char)((src) >> 16); \
(dst)[3] = (unsigned char)((src) >> 24);
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
{
unsigned long used, available;
used = ctx->lo & 0x3f;
ctx->buffer[used++] = 0x80;
available = 64 - used;
if (available < 8) {
memset(&ctx->buffer[used], 0, available);
body(ctx, ctx->buffer, 64);
used = 0;
available = 64;
}
memset(&ctx->buffer[used], 0, available - 8);
ctx->lo <<= 3;
OUT(&ctx->buffer[56], ctx->lo)
OUT(&ctx->buffer[60], ctx->hi)
body(ctx, ctx->buffer, 64);
OUT(&result[0], ctx->a)
OUT(&result[4], ctx->b)
OUT(&result[8], ctx->c)
OUT(&result[12], ctx->d)
memset(ctx, 0, sizeof(*ctx));
}
#endif

102
hailort/CMakeLists.txt Normal file
View File

@@ -0,0 +1,102 @@
cmake_minimum_required(VERSION 3.0.0)
# Set firmware version
add_definitions( -DFIRMWARE_VERSION_MAJOR=4 )
add_definitions( -DFIRMWARE_VERSION_MINOR=6 )
add_definitions( -DFIRMWARE_VERSION_REVISION=0 )
message(STATUS "Building pre_build")
include(${CMAKE_CURRENT_LIST_DIR}/libhailort/cmake/execute_cmake.cmake)
set(HAILO_EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/external)
execute_cmake(
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/pre_build
BUILD_DIR ${CMAKE_CURRENT_LIST_DIR}/pre_build/build
CONFIGURE_ARGS
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_LIST_DIR}/pre_build/install
-DHAILO_EXTERNAL_DIR=${HAILO_EXTERNAL_DIR}
BUILD_ARGS
--config ${CMAKE_BUILD_TYPE} --target install ${CMAKE_EXTRA_BUILD_ARGS}
PARALLEL_BUILD
)
# BENCHMARK_ENABLE_TESTING can be used by other 3rd party projects, therefore we define it
# before adding projects
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library.")
add_subdirectory(external/benchmark EXCLUDE_FROM_ALL)
# Include host protobuf for protoc (https://stackoverflow.com/questions/53651181/cmake-find-protobuf-package-in-custom-directory)
if(CMAKE_HOST_UNIX)
include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/lib/cmake/protobuf/protobuf-config.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/lib/cmake/protobuf/protobuf-module.cmake)
else()
include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/cmake/protobuf-config.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/cmake/protobuf-module.cmake)
endif()
# Add target protobuf directory and exclude its targets from all
# Disable protobuf tests, protoc and MSVC static runtime unless they are already defined
# NOTE: we can also force - set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests" FORCE)
if(NOT protobuf_BUILD_TESTS)
set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
endif()
if(NOT protobuf_BUILD_PROTOC_BINARIES)
set(protobuf_BUILD_PROTOC_BINARIES OFF CACHE BOOL "Build libprotoc and protoc compiler")
endif()
if(MSVC AND NOT protobuf_MSVC_STATIC_RUNTIME)
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Protobuf MSVC static runtime")
endif()
if(NOT protobuf_WITH_ZLIB)
set(protobuf_WITH_ZLIB OFF CACHE BOOL "Compile protobuf with zlib")
endif()
add_subdirectory(external/protobuf/cmake EXCLUDE_FROM_ALL)
if(NOT MSVC)
set_target_properties(libprotobuf PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
set(HAILORT_INC_DIR ${PROJECT_SOURCE_DIR}/hailort/libhailort/include)
set(HAILORT_SRC_DIR ${PROJECT_SOURCE_DIR}/hailort/libhailort/src)
set(HAILORT_COMMON_DIR ${PROJECT_SOURCE_DIR}/hailort/)
set(COMMON_INC_DIR ${PROJECT_SOURCE_DIR}/common/include)
set(DRIVER_INC_DIR ${PROJECT_SOURCE_DIR}/hailort/drivers/common)
if(HAILO_BUILD_PYBIND)
add_subdirectory(external/pybind11 EXCLUDE_FROM_ALL)
endif()
add_subdirectory(external/Catch2 EXCLUDE_FROM_ALL)
add_subdirectory(external/CLI11 EXCLUDE_FROM_ALL)
add_subdirectory(external/json EXCLUDE_FROM_ALL)
add_subdirectory(external/DotWriter EXCLUDE_FROM_ALL)
add_subdirectory(external/spdlog EXCLUDE_FROM_ALL)
set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(common)
add_subdirectory(libhailort)
add_subdirectory(hailortcli)
# copy files to venv
if(HAILO_BUILD_PYBIND AND HAILO_BUILD_PYHAILORT_VENV)
set(VENV_DRIVERS_DIR ${CMAKE_SOURCE_DIR}/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/)
set(PYHAILORT_FILES_TO_COPY
$<TARGET_FILE:_pyhailort>
)
set(VENV_PYHAILORT_INTERNAL_DIR ${CMAKE_SOURCE_DIR}/platform_internals/hailo_platform_internals/pyhailort/)
set(PYHAILORT_INTERNAL_FILES_TO_COPY
$<TARGET_FILE:_pyhailort_internal>
)
add_custom_target(
pyhailort_venv ALL
COMMAND ${CMAKE_COMMAND} -E copy ${PYHAILORT_FILES_TO_COPY} ${VENV_DRIVERS_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${PYHAILORT_INTERNAL_FILES_TO_COPY} ${VENV_PYHAILORT_INTERNAL_DIR}
)
add_dependencies(pyhailort_venv libhailort _pyhailort)
endif()
if(HAILO_WIN_DRIVER)
add_subdirectory(drivers/win)
add_subdirectory(packaging)
endif()
# Compile PCIe Driver if QNX
if(CMAKE_SYSTEM_NAME STREQUAL QNX)
add_subdirectory(drivers/qnx)
endif()

21
hailort/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2020-2022 Hailo Technologies Ltd.
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,13 @@
| Package | Copyright (c) | License | Version | Notes | References |
|:---------------------------------|:----------------------------------|:-------------------|:---------------|:-------------------------------------------|:------------------------------------------------------------------------------|
| CLI11 | University of Cincinnati | 3-Clause BSD | 1.7 | Cloned entire package | https://github.com/CLIUtils/CLI11 |
| Catch2 | Catch2 Authors | BSL-1.0 | 2.13.7 | Cloned entire package | https://github.com/catchorg/Catch2 |
| protobuf | Google Inc. | BSD | 3.11.4 | Cloned entire package | https://github.com/protocolbuffers/protobuf |
| pybind11 | Wenzel Jakob | BSD | 2.3.0 | Cloned entire package | https://github.com/pybind/pybind11 |
| spdlog | Gabi Melman | MIT | 1.6.1 | Cloned entire package | https://github.com/gabime/spdlog |
| folly | Facebook, Inc. and its affiliates | Apache License 2.0 | v2020.08.17.00 | Copied only the file `folly/TokenBucket.h` | https://github.com/facebook/folly |
| nlohmann_json_cmake_fetchcontent | ArthurSonzogni | MIT License | v3.9.1 | Cloned entire package | https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent |
| readerwriterqueue | Cameron Desrochers | Simplified BSD | 1.0.3 | Cloned entire package | https://github.com/cameron314/readerwriterqueue |
| DotWriter | John Vilk | MIT License | master | Cloned entire package (forked) | https://github.com/jvilk/DotWriter |
| benchmark | Google Inc. | Apache License 2.0 | 1.6.0 | Cloned entire package | https://github.com/google/benchmark.git |
| md5 | Alexander Peslyak | cut-down BSD | - | Copied code from website | http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 |

View File

@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.0.0)
if(WIN32)
set(HAILORT_COMMON_OS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/os/windows")
elseif(UNIX)
set(HAILORT_COMMON_OS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/os/posix")
else()
message(FATAL_ERROR "Unexpeced host, stopping build")
endif()
set(HAILORT_COMMON_OS_DIR ${HAILORT_COMMON_OS_DIR} PARENT_SCOPE)
set(SRC_FILES
${HAILORT_COMMON_OS_DIR}/ethernet_utils.cpp
${HAILORT_COMMON_OS_DIR}/filesystem.cpp
${HAILORT_COMMON_OS_DIR}/socket.cpp
${HAILORT_COMMON_OS_DIR}/process.cpp
${CMAKE_CURRENT_SOURCE_DIR}/barrier.cpp
${CMAKE_CURRENT_SOURCE_DIR}/file_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/string_utils.cpp
)
if(WIN32)
# Windows only modules:
set(SRC_FILES ${SRC_FILES}
${HAILORT_COMMON_OS_DIR}/string_conversion.cpp
)
elseif(UNIX)
# Unix only modules
set(SRC_FILES ${SRC_FILES}
${HAILORT_COMMON_OS_DIR}/traffic_control.cpp
)
endif()
set(HAILORT_COMMON_CPP_SOURCES ${SRC_FILES} PARENT_SCOPE)

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file async_thread.hpp
**/
#ifndef _ASYNC_THREAD_HPP_
#define _ASYNC_THREAD_HPP_
#include <functional>
#include <thread>
#include <memory>
#include <atomic>
namespace hailort
{
/**
* Basic implementation of an async result of a function on some new thread. We use this class instead of `std::async`
* because std::async uses future object that store/throw exceptions, and we can't compile it to armv7l platform.
*/
template<typename T>
class AsyncThread final {
public:
explicit AsyncThread(std::function<T(void)> func) :
m_result(),
m_thread([this, func]() {
m_result.store(func());
})
{}
/**
* NOTE! this object is not moveable by purpose, on creation we create a lambda that take `this`, if we
* move the object `this` will change and the callback will be wrong. Use exeternal storage like std::unique_ptr
* to move the object (or to put it inside a container)
*/
AsyncThread(const AsyncThread<T> &) = delete;
AsyncThread(AsyncThread<T> &&other) = delete;
AsyncThread<T>& operator=(const AsyncThread<T>&) = delete;
AsyncThread<T>& operator=(AsyncThread<T> &&) = delete;
T get()
{
if (m_thread.joinable()) {
m_thread.join();
}
return m_result.load();
}
private:
std::atomic<T> m_result;
std::thread m_thread;
};
template<typename T>
using AsyncThreadPtr = std::unique_ptr<AsyncThread<T>>;
} /* namespace hailort */
#endif /* _ASYNC_THREAD_HPP_ */

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file barrier.cpp
**/
#include "common/barrier.hpp"
namespace hailort
{
Barrier::Barrier(size_t count) :
m_original_count(count), m_count(count), m_generation(0), m_mutex(), m_cv(), m_is_activated(true)
{}
void Barrier::arrive_and_wait()
{
// Consider adding timeout and returning HAILO_STATUS
if (!m_is_activated.load()) {
return;
}
std::unique_lock<std::mutex> lock(m_mutex);
size_t current_generation = m_generation;
if ((--m_count) == 0) {
m_generation++;
m_count = m_original_count;
m_cv.notify_all();
}
else {
m_cv.wait(lock, [this, current_generation] { return ((current_generation != m_generation) || !m_is_activated); });
}
}
void Barrier::terminate()
{
m_is_activated.store(false);
m_cv.notify_all();
}
} /* namespace hailort */

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file barrier.hpp
**/
#ifndef _BARRIER_HPP_
#define _BARRIER_HPP_
#include <mutex>
#include <condition_variable>
#include <atomic>
namespace hailort
{
/**
* A barrier is a synchronization object that allows an expected number of threads to block until all of them
* arrive at the barrier.
*
* This class should be similar to std::barrier that will be released in c++20 (If we use c++20, please delete this class)
*/
class Barrier final {
public:
explicit Barrier(size_t count);
Barrier(const Barrier &) = delete;
Barrier& operator=(const Barrier &) = delete;
Barrier(Barrier &&) = delete;
Barrier& operator=(Barrier &&) = delete;
/**
* Decreases the count by 1 and blocks until all threads arrives.
*/
void arrive_and_wait();
/**
* Signal all blocking occurrences, and further calls to 'arrive_and_wait()' will return immediately
*/
void terminate();
private:
const size_t m_original_count;
size_t m_count;
size_t m_generation;
std::mutex m_mutex;
std::condition_variable m_cv;
std::atomic_bool m_is_activated;
};
} /* namespace hailort */
#endif /* _BARRIER_HPP_ */

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file circular_buffer.hpp
* @brief
*
**/
#ifndef __CIRCULAR_BUFFER_HEADER__
#define __CIRCULAR_BUFFER_HEADER__
#include "hailo/platform.h"
#include "common/utils.hpp"
#include <array>
namespace hailort
{
typedef struct {
volatile int head;
volatile int tail;
int size;
int size_mask;
} circbuf_t;
//TODO: Do not change the behavior of this module. see PLDA descs impl..
//TODO: optimize macros
#ifndef MIN
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#endif
#ifdef _WIN32
#define _CB_FETCH(x) (InterlockedOr((LONG volatile*)(&x), (LONG)0))
#define _CB_SET(x, value) (InterlockedExchange((LONG volatile*)(&x), (LONG)(value)))
#else
#define _CB_FETCH(x) (__sync_fetch_and_or(&(x), 0))
#define _CB_SET(x, value) ((void)__sync_lock_test_and_set(&(x), value))
#endif
#define CB_INIT(circbuf, s) \
(circbuf).head = 0; \
(circbuf).tail = 0; \
(circbuf).size = static_cast<int>(s); \
(circbuf).size_mask = static_cast<int>((s) - 1)
#define CB_RESET(circbuf) \
(circbuf).head = 0; \
(circbuf).tail = 0
#define CB_HEAD(x) _CB_FETCH((x).head)
#define CB_TAIL(x) _CB_FETCH((x).tail)
#define CB_SIZE(x) _CB_FETCH((x).size)
#define CB_ENQUEUE(circbuf, value) _CB_SET((circbuf).head, ((circbuf).head + (value)) & ((circbuf).size_mask))
#define CB_DEQUEUE(circbuf, value) _CB_SET((circbuf).tail, ((circbuf).tail + (value)) & ((circbuf).size_mask))
#define CB_AVAIL(circbuf, head, tail) ((((circbuf).size)-1+(tail)-(head)) & ((circbuf).size_mask))
#define CB_AVAIL_CONT(circbuf, head, tail) \
MIN(CB_AVAIL((circbuf), (head), (tail)), (circbuf).size - (head))
#define CB_PROG(circbuf, head, tail) ((((circbuf).size)+(head)-(tail)) & ((circbuf).size_mask))
#define CB_PROG_CONT(circbuf, head, tail) \
MIN(CB_PROG((circbuf), (head), (tail)), (circbuf).size - (tail))
// TODO: implement more functionalities, better move semantic handle
// TODO: support consts methods (front(), empty()), right now CB_* macros requires non const pointer to head+tail
template<typename T>
class CircularArray final
{
public:
static_assert(std::is_pod<T>::value, "CircularArray can be used only with POD type");
CircularArray(size_t storage_size)
{
// storage size must be a power of 2
assert(is_powerof2(storage_size));
CB_INIT(m_circ, storage_size);
m_array.resize(storage_size);
}
void push_back(const T& element)
{
// assert(CB_AVAIL(m_circ, CB_HEAD(m_circ), CB_TAIL(m_circ)));
m_array[CB_HEAD(m_circ)] = element;
CB_ENQUEUE(m_circ, 1);
}
void pop_front()
{
CB_DEQUEUE(m_circ, 1);
}
T& front()
{
return m_array[CB_TAIL(m_circ)];
}
bool empty()
{
return CB_HEAD(m_circ) == CB_TAIL(m_circ);
}
private:
circbuf_t m_circ;
std::vector<T> m_array;
};
} /* namespace hailort */
#endif /* __CIRCULAR_BUFFER_HEADER__ */

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file compiler_extensions_compat.hpp
* @brief Defines compiler extensions and preprocessor macros that are compatible across MSVC and GNU compilers
**/
#ifndef __COMPILER_EXTENSIONS_COMPAT_HPP__
#define __COMPILER_EXTENSIONS_COMPAT_HPP__
// https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc
// Initializer/finalizer sample for MSVC and GCC/Clang.
// 2010-2016 Joe Lowe. Released into the public domain.
#ifdef __cplusplus
#define COMPAT__INITIALIZER(f) \
static void f(void); \
struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
static void f(void)
#elif defined(_MSC_VER)
#pragma section(".CRT$XCU",read)
#define INITIALIZER2_(f,p) \
static void f(void); \
__declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
__pragma(comment(linker,"/include:" p #f "_")) \
static void f(void)
#ifdef _WIN64
#define COMPAT__INITIALIZER(f) INITIALIZER2_(f,"")
#else
#define COMPAT__INITIALIZER(f) INITIALIZER2_(f,"_")
#endif
#else
#define COMPAT__INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif
#if !defined(UNREFERENCED_PARAMETER) && !defined(_MSC_VER)
#define UNREFERENCED_PARAMETER(param) \
do { \
(void)(param); \
} while(0)
#endif
// Used to capture consts in lambda expressions. On mscv constants must be captures, while in clang it
// causes a warning
#if _MSC_VER
#define LAMBDA_CONSTANT(constant_name) constant_name
#else
#define LAMBDA_CONSTANT(constant_name) constant_name=constant_name
#endif
#endif /* __COMPILER_EXTENSIONS_COMPAT_HPP__ */

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file ethernet_utils.hpp
* @brief TODO
*
* TODO
**/
#ifndef __OS_ETHERNET_UTILS_H__
#define __OS_ETHERNET_UTILS_H__
#include <hailo/hailort.h>
#include "hailo/expected.hpp"
#if defined(_MSC_VER)
#include <unordered_map>
#include <ifmib.h>
namespace hailort
{
class NetworkInterface;
using NetworkInterfaces = std::vector<NetworkInterface>;
class NetworkInterface final
{
public:
NetworkInterface(uint32_t index, const std::string& name, const std::string& friendly_name, const std::string& ip);
~NetworkInterface() = default;
uint32_t index() const;
std::string name() const;
std::string friendly_name() const;
std::string ip() const;
static Expected<NetworkInterfaces> get_all_interfaces();
private:
const uint32_t m_index;
const std::string m_name;
const std::string m_friendly_name;
const std::string m_ip;
};
static const uint32_t MacAddressSize = 6;
using MacAddress = std::array<uint8_t, MacAddressSize>;
class ArpTable final
{
public:
~ArpTable() = default;
Expected<MacAddress> get_mac_address(uint32_t ip) const;
static Expected<ArpTable> create(uint32_t interface_index);
private:
ArpTable(const std::unordered_map<uint32_t, MacAddress>& table);
std::unordered_map<uint32_t, MacAddress> m_table;
};
} /* namespace hailort */
#else
#include <net/if.h>
#endif
namespace hailort
{
class EthernetUtils final
{
public:
EthernetUtils() = delete;
#if defined(_MSC_VER)
static const uint32_t MAX_INTERFACE_SIZE = MAX_INTERFACE_NAME_LEN;
#else
static const uint32_t MAX_INTERFACE_SIZE = IFNAMSIZ;
#endif
static hailo_status get_interface_from_board_ip(const char *board_ip, char *interface_name, size_t interface_name_length);
static hailo_status get_ip_from_interface(const char *interface_name, char *ip, size_t ip_length);
private:
#if defined(__GNUG__)
static hailo_status get_interface_from_arp_entry(char *arp_entry, char *interface_name,
size_t max_interface_name_length);
#endif
};
} /* namespace hailort */
#endif /* __OS_ETHERNET_UTILS_H__ */

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file file_utils.cpp
* @brief Utilities for file operations
**/
#include "common/file_utils.hpp"
#include "common/utils.hpp"
#include <fstream>
namespace hailort
{
Expected<size_t> get_istream_size(std::ifstream &s)
{
auto beg_pos = s.tellg();
CHECK_AS_EXPECTED(-1 != beg_pos, HAILO_FILE_OPERATION_FAILURE, "ifstream::tellg() failed");
s.seekg(0, s.end);
CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "ifstream::seekg() failed");
auto size = s.tellg();
CHECK_AS_EXPECTED(-1 != size, HAILO_FILE_OPERATION_FAILURE, "ifstream::tellg() failed");
s.seekg(beg_pos, s.beg);
CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "ifstream::seekg() failed");
auto total_size = static_cast<uint64_t>(size - beg_pos);
CHECK_AS_EXPECTED(total_size <= std::numeric_limits<size_t>::max(), HAILO_FILE_OPERATION_FAILURE,
"File size {} is too big", total_size);
return Expected<size_t>(static_cast<size_t>(total_size));
}
Expected<Buffer> read_binary_file(const std::string &file_path)
{
std::ifstream file(file_path, std::ios::in | std::ios::binary);
CHECK_AS_EXPECTED(file.good(), HAILO_OPEN_FILE_FAILURE, "Error opening file {}", file_path);
auto file_size = get_istream_size(file);
CHECK_EXPECTED(file_size, "Failed to get file size");
auto buffer = Buffer::create(file_size.value());
CHECK_EXPECTED(buffer, "Failed to allocate file buffer ({} bytes}", file_size.value());
// Read the data
file.read(reinterpret_cast<char*>(buffer->data()), buffer->size());
CHECK_AS_EXPECTED(file.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading file {}", file_path);
return buffer.release();
}
} /* namespace hailort */

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file file_utils.hpp
* @brief Utilities for file operations
**/
#ifndef _HAILO_FILE_UTILS_HPP_
#define _HAILO_FILE_UTILS_HPP_
#include "hailo/expected.hpp"
#include "hailo/buffer.hpp"
namespace hailort
{
/**
* Returns the amount of data left in the given file.
*/
Expected<size_t> get_istream_size(std::ifstream &s);
/**
* Reads full file content into a `Buffer`
*/
Expected<Buffer> read_binary_file(const std::string &file_path);
} /* namespace hailort */
#endif /* _HAILO_FILE_UTILS_HPP_ */

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file filesystem.hpp
* @brief File system API
**/
#ifndef _OS_FILESYSTEM_HPP_
#define _OS_FILESYSTEM_HPP_
#include "hailo/hailort.h"
#include "hailo/platform.h"
#include "hailo/expected.hpp"
#include <vector>
#include <string>
#if defined(__GNUC__)
#include <dirent.h>
#endif
namespace hailort
{
class Filesystem final {
public:
Filesystem() = delete;
static Expected<std::vector<std::string>> get_files_in_dir_flat(const std::string &dir_path);
static Expected<bool> is_directory(const std::string &path);
static bool has_suffix(const std::string &file_name, const std::string &suffix)
{
return (file_name.size() >= suffix.size()) && equal(suffix.rbegin(), suffix.rend(), file_name.rbegin());
}
private:
// OS-specific filesystem directory separator char (i.e. backslash on Windows or forward slash on UNIX)
static const char *SEPARATOR;
#if defined(_MSC_VER)
struct FileInfo {
std::string path;
DWORD attrs;
};
static bool is_regular_or_readonly_file(DWORD attrs);
// TODO: supoport unicode
class FindFile final {
public:
static Expected<FindFile> create(const std::string &dir_path);
~FindFile();
FindFile(const FindFile &other) = delete;
FindFile &operator=(const FindFile &other) = delete;
FindFile &operator=(FindFile &&other) = delete;
FindFile(FindFile &&other);
Filesystem::FileInfo get_cur_file_info();
// Will return HAILO_INVALID_OPERATION when the iteration is complete or HAILO_FILE_OPERATION_FAILURE upon failure
hailo_status next_file();
private:
FindFile(HANDLE find_hadle, const WIN32_FIND_DATAA &find_data);
HANDLE m_find_handle;
WIN32_FIND_DATAA m_find_data;
};
#else
class DirWalker final {
public:
static Expected<DirWalker> create(const std::string &dir_path);
~DirWalker();
DirWalker(const DirWalker &other) = delete;
DirWalker &operator=(const DirWalker &other) = delete;
DirWalker &operator=(DirWalker &&other) = delete;
DirWalker(DirWalker &&other);
dirent* next_file();
private:
DirWalker(DIR *dir, const std::string &dir_path);
DIR *m_dir;
const std::string m_path_string;
};
#endif
};
} /* namespace hailort */
#endif /* _OS_FILESYSTEM_HPP_ */

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file latency_meter.hpp
* @brief Calculate inference frame latency
**/
#ifndef _HAILO_LATENCY_METER_HPP_
#define _HAILO_LATENCY_METER_HPP_
#include "hailo/expected.hpp"
#include "common/circular_buffer.hpp"
#include <set>
#include <mutex>
#include <unordered_map>
namespace hailort
{
/**
* Used to measure latency of hailo datastream - the average amount of time between
* the start of the action to the end of the last stream.
*/
class LatencyMeter final {
public:
using duration = std::chrono::nanoseconds;
using TimestampsArray = CircularArray<duration>;
explicit LatencyMeter(const std::set<uint32_t> &output_channels, size_t timestamps_list_length) :
m_start_timestamps(timestamps_list_length),
m_latency_count(0),
m_latency_sum(0)
{
for (uint32_t ch : output_channels) {
m_end_timestamps_per_channel.emplace(ch, TimestampsArray(timestamps_list_length));
}
}
/**
* Adds the given timestamp as a start.
* @note Assumes it is the only thread that is calling the function
*/
void add_start_sample(duration timestamp)
{
m_start_timestamps.push_back(timestamp);
update_latency();
}
/*
* Adds the given timestamp as the end of the given channel. The operation is done
* after this function is called on all channels.
* @note Assumes that only one thread per channel is calling this function.
*/
void add_end_sample(uint32_t channel_index, duration timestamp)
{
// Safe to access from several threads (when each pass different channel) because the map cannot
// be changed in runtime.
assert(m_end_timestamps_per_channel.find(channel_index) != m_end_timestamps_per_channel.end());
m_end_timestamps_per_channel.at(channel_index).push_back(timestamp);
update_latency();
}
/**
* Queries average latency. One can clear measured latency by passing clear=true.
*/
Expected<duration> get_latency(bool clear)
{
std::lock_guard<std::mutex> lock_guard(m_lock);
if (m_latency_count == 0) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
duration latency = (m_latency_sum / m_latency_count);
if (clear) {
m_latency_sum = duration();
m_latency_count = 0;
}
return latency;
}
private:
void update_latency()
{
std::lock_guard<std::mutex> lock_guard(m_lock);
if (m_start_timestamps.empty()) {
// wait for begin sample
return;
}
duration start = m_start_timestamps.front();
duration end(0);
for (auto &end_timesatmps : m_end_timestamps_per_channel) {
if (end_timesatmps.second.empty()) {
// Wait for all channel samples
return;
}
end = std::max(end, end_timesatmps.second.front());
}
// calculate the latency
m_latency_sum += (end - start);
m_latency_count++;
// pop fronts
m_start_timestamps.pop_front();
for (auto &end_timesatmps : m_end_timestamps_per_channel) {
end_timesatmps.second.pop_front();
}
}
std::mutex m_lock;
TimestampsArray m_start_timestamps;
std::unordered_map<uint32_t, TimestampsArray> m_end_timestamps_per_channel;
size_t m_latency_count;
duration m_latency_sum;
};
using LatencyMeterPtr = std::shared_ptr<LatencyMeter>;
} /* namespace hailort */
#endif /* _HAILO_LATENCY_METER_HPP_ */

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file logger_macros.hpp
* @brief Declares logger macros used by hailort components.
* Assumes spdlog::set_default_logger was called (otherwise uses spdlog default logger)
**/
#ifndef _LOGGER_MACROS_HPP_
#define _LOGGER_MACROS_HPP_
#include "hailo/hailort.h"
#define SPDLOG_NO_EXCEPTIONS
/* Minimum log level availble at compile time */
#ifndef SPDLOG_ACTIVE_LEVEL
#ifndef NDEBUG
#define SPDLOG_ACTIVE_LEVEL (SPDLOG_LEVEL_DEBUG)
#else
#define SPDLOG_ACTIVE_LEVEL (SPDLOG_LEVEL_INFO)
#endif
#endif
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
inline std::ostream& operator<<(std::ostream& os, const hailo_status& status)
{
auto status_str = hailo_get_status_message(status);
if (status_str == nullptr) {
return os << "<Invalid(" << static_cast<int>(status) << ")>";
}
return os << status_str << "(" << static_cast<int>(status) << ")";
}
namespace hailort
{
// Makes sure during compilation time that all strings in LOGGER__X macros are not in printf format, but in fmtlib format.
constexpr bool string_not_printf_format(char const * str) {
int i = 0;
while (str[i] != '\0') {
if (str[i] == '%' && ((str[i+1] >= 'a' && str[i+1] <= 'z') || (str[i+1] >= 'A' && str[i+1] <= 'Z'))) {
return false;
}
i++;
}
return true;
}
#define EXPAND(x) x
#define ASSERT_NOT_PRINTF_FORMAT(fmt, ...) static_assert(string_not_printf_format(fmt), "Error - Log string is in printf format and not in fmtlib format!")
#define LOGGER_TO_SPDLOG(level, ...)\
do{\
EXPAND(ASSERT_NOT_PRINTF_FORMAT(__VA_ARGS__));\
level(__VA_ARGS__);\
} while(0) // NOLINT: clang complains about this code never executing
#define LOGGER__TRACE(...) LOGGER_TO_SPDLOG(SPDLOG_TRACE, __VA_ARGS__)
#define LOGGER__DEBUG(...) LOGGER_TO_SPDLOG(SPDLOG_DEBUG, __VA_ARGS__)
#define LOGGER__INFO(...) LOGGER_TO_SPDLOG(SPDLOG_INFO, __VA_ARGS__)
#define LOGGER__WARN(...) LOGGER_TO_SPDLOG(SPDLOG_WARN, __VA_ARGS__)
#define LOGGER__WARNING LOGGER__WARN
#define LOGGER__ERROR(...) LOGGER_TO_SPDLOG(SPDLOG_ERROR, __VA_ARGS__)
#define LOGGER__CRITICAL(...) LOGGER_TO_SPDLOG(SPDLOG_CRITICAL, __VA_ARGS__)
} /* namespace hailort */
#endif /* _LOGGER_MACROS_HPP_ */

View File

@@ -0,0 +1,161 @@
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include "utils.h"
#include <hailo/hailort.h>
#include "common/utils.hpp"
#include "common/logger_macros.hpp"
#include "common/ethernet_utils.hpp"
namespace hailort
{
#define ETHERNET_UTILS__ARP_FILE ("/proc/net/arp")
#define ETHERNET_UTILS__ARP_ENTRY_DELIMIETERS (" ,\n")
#define ETHERNET_UTILS__ARP_MAX_ENTRY_LENGTH (512)
#define ETHERNET_UTILS__ARP_DEVICE_NAME_INDEX (4)
hailo_status EthernetUtils::get_interface_from_arp_entry(char *arp_entry, char *interface_name,
size_t max_interface_name_length)
{
/* This function parses the interface name out from the arp entry
* Each entry is built as follows:
* IP address HW type Flags HW address Mask Device
*
* For example:
* 10.0.0.163 0x1 0x2 80:00:de:ad:be:3f * enp1s0
* */
hailo_status status = HAILO_UNINITIALIZED;
size_t token_counter = 0;
char* token = NULL;
/* Start splitting the arp entry into tokens according to the delimiter */
token = strtok(arp_entry, ETHERNET_UTILS__ARP_ENTRY_DELIMIETERS);
if (NULL == token) {
LOGGER__ERROR("Invalid arp entry, could not split it to tokens");
status = HAILO_ETH_FAILURE;
goto l_exit;
}
/* Iterate over the tokens until the device name is found */
while (NULL != token) {
token = strtok(NULL, ETHERNET_UTILS__ARP_ENTRY_DELIMIETERS);
if (ETHERNET_UTILS__ARP_DEVICE_NAME_INDEX == token_counter) {
LOGGER__DEBUG("Interface name: {}", token);
strncpy(interface_name, token, max_interface_name_length);
break;
}
token_counter++;
}
status = HAILO_SUCCESS;
l_exit:
return status;
}
hailo_status EthernetUtils::get_interface_from_board_ip(const char *board_ip, char *interface_name, size_t interface_name_length)
{
hailo_status status = HAILO_UNINITIALIZED;
FILE* arp_file = NULL;
int fclose_rc = -1;
char buffer[ETHERNET_UTILS__ARP_MAX_ENTRY_LENGTH] = {};
CHECK_ARG_NOT_NULL(interface_name);
CHECK_ARG_NOT_NULL(board_ip);
/* Open arp file */
arp_file = fopen(ETHERNET_UTILS__ARP_FILE, "r");
if (NULL == arp_file) {
LOGGER__ERROR("Cannot open file {}. Errno: {:#x}", ETHERNET_UTILS__ARP_FILE, errno);
status = HAILO_OPEN_FILE_FAILURE;
goto l_exit;
}
/* Go over all of the lines at the file */
while(fgets(buffer, ARRAY_LENGTH(buffer), arp_file)) {
/* Check if the arp line contains the board_ip */
if (strstr(buffer, board_ip)) {
status = get_interface_from_arp_entry(buffer, interface_name, interface_name_length);
if (HAILO_SUCCESS != status) {
goto l_exit;
}
break;
}
}
status = HAILO_SUCCESS;
l_exit:
if (NULL != arp_file) {
fclose_rc = fclose(arp_file);
if (0 != fclose_rc) {
LOGGER__ERROR("Cannot close arp file {} ", ETHERNET_UTILS__ARP_FILE);
if (HAILO_SUCCESS == status) {
status = HAILO_CLOSE_FAILURE;
} else {
LOGGER__ERROR("Did not override status. Left status value at: {} (not assigned {}",
status,
HAILO_CLOSE_FAILURE);
}
}
}
return status;
}
hailo_status EthernetUtils::get_ip_from_interface(const char *interface_name, char *ip, size_t ip_length)
{
hailo_status status = HAILO_UNINITIALIZED;
struct ifreq ifr = {};
int fd = 0;
int posix_rc = 0;
CHECK_ARG_NOT_NULL(interface_name);
CHECK_ARG_NOT_NULL(ip);
/* Create socket */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
LOGGER__ERROR("Failed to create socket. Errno: {:#x}", errno);
status = HAILO_ETH_FAILURE;
goto l_exit;
}
/* Convert interface name to ip address */
ifr.ifr_addr.sa_family = AF_INET;
(void)strncpy(ifr.ifr_name, interface_name, IFNAMSIZ-1);
posix_rc = ioctl(fd, SIOCGIFADDR, &ifr);
if (0 > posix_rc) {
LOGGER__ERROR("Interface was not found. ioctl with SIOCGIFADDR has failed. Errno: {:#x}", errno);
status = HAILO_ETH_INTERFACE_NOT_FOUND;
goto l_exit;
}
(void)strncpy(ip, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), ip_length);
LOGGER__DEBUG("Interface {} | IP: {}", interface_name, ip);
status = HAILO_SUCCESS;
l_exit:
/* Close the socket if it was created */
if (0 < fd) {
posix_rc = close(fd);
if (0 != posix_rc) {
LOGGER__ERROR("Failed closing socket. Errno: {:#x}", errno);
/* Update status if only in case there was not previous error */
if (HAILO_SUCCESS == status) {
status = HAILO_CLOSE_FAILURE;
} else {
LOGGER__ERROR("Did not override status. Left status value at: {} (not assigned {}",
status,
HAILO_CLOSE_FAILURE);
}
}
}
return status;
}
} /* namespace hailort */

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file filesystem.cpp
* @brief Filesystem wrapper for Linux
**/
#include "common/filesystem.hpp"
#include "common/logger_macros.hpp"
#include "common/utils.hpp"
#include <errno.h>
#include <sys/stat.h>
namespace hailort
{
const char *Filesystem::SEPARATOR = "/";
Expected<Filesystem::DirWalker> Filesystem::DirWalker::create(const std::string &dir_path)
{
DIR *dir = opendir(dir_path.c_str());
CHECK(nullptr != dir, make_unexpected(HAILO_FILE_OPERATION_FAILURE),
"Could not open directory \"{}\" with errno {}", dir_path, errno);
return DirWalker(dir, dir_path);
}
Filesystem::DirWalker::DirWalker(DIR *dir, const std::string &dir_path) :
m_dir(dir),
m_path_string(dir_path)
{}
Filesystem::DirWalker::~DirWalker()
{
if (nullptr != m_dir) {
const auto result = closedir(m_dir);
if (-1 == result) {
LOGGER__ERROR("closedir on directory \"{}\" failed with errno {}", m_path_string.c_str(), errno);
}
}
}
Filesystem::DirWalker::DirWalker(DirWalker &&other) :
m_dir(std::exchange(other.m_dir, nullptr)),
m_path_string(other.m_path_string)
{}
dirent* Filesystem::DirWalker::next_file()
{
return readdir(m_dir);
}
#if defined(__unix__)
Expected<std::vector<std::string>> Filesystem::get_files_in_dir_flat(const std::string &dir_path)
{
const std::string dir_path_with_sep = has_suffix(dir_path, SEPARATOR) ? dir_path : dir_path + SEPARATOR;
auto dir = DirWalker::create(dir_path_with_sep);
CHECK_EXPECTED(dir);
std::vector<std::string> files;
struct dirent *entry = nullptr;
while ((entry = dir->next_file()) != nullptr) {
if (entry->d_type != DT_REG) {
continue;
}
const std::string file_name = entry->d_name;
files.emplace_back(dir_path_with_sep + file_name);
}
return files;
}
// QNX
#elif defined(__QNX__)
Expected<std::vector<std::string>> Filesystem::get_files_in_dir_flat(const std::string &dir_path)
{
(void) dir_path;
return make_unexpected(HAILO_NOT_IMPLEMENTED);
}
// Unsupported Platform
#else
static_assert(false, "Unsupported Platform!");
#endif
Expected<bool> Filesystem::is_directory(const std::string &path)
{
struct stat path_stat{};
CHECK(0 == stat(path.c_str(), &path_stat), make_unexpected(HAILO_FILE_OPERATION_FAILURE),
"stat() on path \"{}\" failed. errno {}", path.c_str(), errno);
return S_ISDIR(path_stat.st_mode);
}
} /* namespace hailort */

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file process.cpp
* @brief Process wrapper for Linux
**/
#include "common/process.hpp"
#include "hailo/hailort.h"
#include "common/utils.hpp"
#include "hailo/buffer.hpp"
namespace hailort
{
Expected<std::pair<int32_t, std::string>> Process::create_and_wait_for_output(const std::string &command_line, uint32_t max_output_size)
{
auto popen_expected = PopenWrapper::create(command_line);
CHECK_EXPECTED(popen_expected);
const auto output_expected = popen_expected->read_stdout(max_output_size);
CHECK_EXPECTED(output_expected);
const auto process_exit_code = popen_expected->close();
return std::make_pair(process_exit_code, output_expected.value());
}
Expected<Process::PopenWrapper> Process::PopenWrapper::create(const std::string &command_line)
{
hailo_status status = HAILO_UNINITIALIZED;
PopenWrapper popen(command_line, status);
CHECK_SUCCESS_AS_EXPECTED(status);
return popen;
}
Process::PopenWrapper::PopenWrapper(const std::string &command_line, hailo_status &status) :
m_command_line(command_line)
{
static const char* READONLY_MODE = "r";
m_pipe = popen(command_line.c_str(), READONLY_MODE);
if (nullptr == m_pipe) {
LOGGER__ERROR("popen(\"{}\") failed with errno={}", command_line.c_str(), errno);
status = HAILO_INTERNAL_FAILURE;
} else {
status = HAILO_SUCCESS;
}
}
Process::PopenWrapper::~PopenWrapper()
{
if (nullptr != m_pipe) {
(void) close();
}
}
Process::PopenWrapper::PopenWrapper(PopenWrapper &&other) :
m_pipe(std::exchange(other.m_pipe, nullptr))
{}
Expected<std::string> Process::PopenWrapper::read_stdout(uint32_t max_output_size)
{
assert (nullptr != m_pipe);
// We zero out the bufer so that output won't contain junk from the heap
auto output = Buffer::create(max_output_size, 0);
CHECK_EXPECTED(output);
const auto num_read = fread(reinterpret_cast<char*>(output->data()), sizeof(uint8_t), output->size(), m_pipe);
if (num_read != output->size()) {
if (feof(m_pipe)) {
// We remove the trailing newline we get from fread
const auto output_as_str = output->to_string();
if (output_as_str[output_as_str.length() - 1] == '\n') {
return output_as_str.substr(0, num_read - 1);
}
return output_as_str.substr(0, num_read);
} else {
LOGGER__ERROR("fread failed with ferror={}", ferror(m_pipe));
return make_unexpected(HAILO_INTERNAL_FAILURE);
}
} else {
// Truncate output
LOGGER__TRACE("Truncating output to {} chars long", max_output_size);
return output->to_string();
}
}
int32_t Process::PopenWrapper::close()
{
assert (nullptr != m_pipe);
const auto return_code = pclose(m_pipe);
m_pipe = nullptr; // We only close the handle once
return return_code;
}
} /* namespace hailort */

View File

@@ -0,0 +1,304 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file socket.cpp
* @brief Socket wrapper for Unix
**/
#include "common/socket.hpp"
#include <arpa/inet.h>
#include <unistd.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <array>
namespace hailort
{
#define LINUX_RMEM_MAX_PATH "/proc/sys/net/core/rmem_max"
hailo_status Socket::SocketModuleWrapper::init_module()
{
return HAILO_SUCCESS;
}
hailo_status Socket::SocketModuleWrapper::free_module()
{
return HAILO_SUCCESS;
}
Expected<Socket> Socket::create(int af, int type, int protocol)
{
auto module_wrapper = SocketModuleWrapper::create();
CHECK_EXPECTED(module_wrapper);
auto socket_fd = create_socket_fd(af, type, protocol);
CHECK_EXPECTED(socket_fd);
auto obj = Socket(module_wrapper.release(), socket_fd.release());
return obj;
}
Socket::Socket(SocketModuleWrapper &&module_wrapper, const socket_t socket_fd) :
m_module_wrapper(std::move(module_wrapper)), m_socket_fd(socket_fd)
{
}
Socket::~Socket()
{
auto status = close_socket_fd();
if (HAILO_SUCCESS != status) {
LOGGER__ERROR("Failed to free socket fd with status {}", status);
}
}
Expected<socket_t> Socket::create_socket_fd(int af, int type, int protocol)
{
socket_t local_socket = INVALID_SOCKET;
local_socket = socket(af, type, protocol);
CHECK_VALID_SOCKET_AS_EXPECTED(local_socket);
return local_socket;
}
hailo_status Socket::close_socket_fd()
{
if (INVALID_SOCKET != m_socket_fd) {
int socket_rc = close(m_socket_fd);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to close socket. errno={}", errno);
}
return HAILO_SUCCESS;
}
hailo_status Socket::abort()
{
int socket_rc = shutdown(m_socket_fd, SHUT_RDWR);
CHECK((0 == socket_rc) || ((-1 == socket_rc) && (ENOTCONN == errno)), HAILO_ETH_FAILURE, "Failed to shutdown (abort) socket. errno={}", errno);
return HAILO_SUCCESS;
}
hailo_status Socket::socket_bind(const sockaddr *addr, socklen_t len)
{
int socket_rc = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(addr);
socket_rc = bind(m_socket_fd, addr, len);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to bind socket. errno={}", errno);
return HAILO_SUCCESS;
}
hailo_status Socket::get_sock_name(sockaddr *addr, socklen_t *len)
{
int socket_rc = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(addr);
CHECK_ARG_NOT_NULL(len);
socket_rc = getsockname(m_socket_fd, addr, len);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed getsockname. errno={}", errno);
return HAILO_SUCCESS;
}
hailo_status Socket::ntop(int af, const void *src, char *dst, socklen_t size)
{
CHECK_ARG_NOT_NULL(src);
CHECK_ARG_NOT_NULL(dst);
CHECK(NULL != inet_ntop(af, src, dst, size), HAILO_ETH_FAILURE,
"Could not convert sockaddr struct to string ip address");
return HAILO_SUCCESS;
}
hailo_status Socket::pton(int af, const char *src, void *dst)
{
int inet_rc = 0;
CHECK_ARG_NOT_NULL(src);
CHECK_ARG_NOT_NULL(dst);
inet_rc = inet_pton(af, reinterpret_cast<const char*>(src), dst);
CHECK(1 == inet_rc, HAILO_ETH_FAILURE, "Could not convert string ip address to sockaddr struct");
return HAILO_SUCCESS;
}
hailo_status Socket::set_recv_buffer_size_max()
{
int socket_rc = SOCKET_ERROR;
FILE *rmem_max_file = NULL;
uint8_t rmem_max_buffer[20] = {};
uint64_t rmem_max = 0;
int file_status = 0;
size_t bytes_read = 0;
rmem_max_file = fopen(LINUX_RMEM_MAX_PATH, "r");
if (NULL != rmem_max_file) {
bytes_read = fread(rmem_max_buffer, sizeof(rmem_max_buffer), sizeof(*rmem_max_buffer), rmem_max_file);
if ((0 != bytes_read) || (feof(rmem_max_file))) {
rmem_max = strtoul((char *)rmem_max_buffer, NULL, 10);
}
if (0 == rmem_max) {
LOGGER__WARN("Could not read rmem_max value from file '{}'", LINUX_RMEM_MAX_PATH);
rmem_max = UINT64_MAX;
}
file_status = fclose(rmem_max_file);
if (0 != file_status) {
LOGGER__WARN("Could not close file '{}' errno - {}.", LINUX_RMEM_MAX_PATH, errno);
}
} else {
LOGGER__WARN("Could not open file '{}' to read rmem_max value.", LINUX_RMEM_MAX_PATH);
}
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVBUF, &rmem_max, sizeof(rmem_max));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set the rcv socket buffer to {}", rmem_max);
return HAILO_SUCCESS;
}
hailo_status Socket::set_timeout(std::chrono::milliseconds timeout_ms, timeval_t *timeout)
{
int socket_rc = SOCKET_ERROR;
time_t seconds = 0;
suseconds_t microseconds = 0;
auto timeout_value = static_cast<uint32_t>(timeout_ms.count());
/* Validate arguments */
CHECK_ARG_NOT_NULL(timeout);
seconds = (timeout_value / MILLISECONDS_IN_SECOND);
microseconds = (timeout_value % MILLISECONDS_IN_SECOND) * MICROSECONDS_IN_MILLISECOND;
timeout->tv_sec = seconds;
timeout->tv_usec = microseconds;
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set receive timeout. Seconds: {}, microseconds {}", seconds,
microseconds);
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_SNDTIMEO, timeout, sizeof(*timeout));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set send timeout. Seconds: {}, microseconds {}", seconds,
microseconds);
return HAILO_SUCCESS;
}
hailo_status Socket::enable_broadcast()
{
int socket_rc = SOCKET_ERROR;
int enable_broadcast = 1;
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_BROADCAST, &enable_broadcast, sizeof(enable_broadcast));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set socket to be broadcast");
return HAILO_SUCCESS;
}
hailo_status Socket::send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent)
{
ssize_t number_of_sent_bytes = 0;
/* Validate arguments */
CHECK_ARG_NOT_NULL(src_buffer);
CHECK_ARG_NOT_NULL(dest_addr);
CHECK_ARG_NOT_NULL(bytes_sent);
number_of_sent_bytes = sendto(m_socket_fd, src_buffer, src_buffer_size, flags,
dest_addr, dest_addr_size);
if (-1 == number_of_sent_bytes) {
if ((EWOULDBLOCK == errno) || (EAGAIN == errno)) {
LOGGER__ERROR("Udp send timeout");
return HAILO_TIMEOUT;
} else if (EINTR == errno) {
LOGGER__ERROR("Udp send interrupted!");
return HAILO_INTERRUPTED_BY_SIGNAL;
} else if (EPIPE == errno) {
// When socket is aborted from another thread sendto will return errno EPIPE
LOGGER__INFO("Udp send aborted!");
return HAILO_STREAM_INTERNAL_ABORT;
} else {
LOGGER__ERROR("Udp failed to send data, errno:{}.", errno);
return HAILO_ETH_SEND_FAILURE;
}
}
*bytes_sent = (size_t)number_of_sent_bytes;
return HAILO_SUCCESS;
}
hailo_status Socket::recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_received, bool log_timeouts_in_debug)
{
ssize_t number_of_received_bytes = 0;
socklen_t result_src_addr_size = src_addr_size;
/* Validate arguments */
CHECK_ARG_NOT_NULL(dest_buffer);
CHECK_ARG_NOT_NULL(src_addr);
CHECK_ARG_NOT_NULL(bytes_received);
number_of_received_bytes = recvfrom(m_socket_fd, dest_buffer, dest_buffer_size, flags,
src_addr, &result_src_addr_size);
if (-1 == number_of_received_bytes) {
if ((EWOULDBLOCK == errno) || (EAGAIN == errno)) {
if (log_timeouts_in_debug) {
LOGGER__DEBUG("Udp recvfrom failed with timeout");
} else {
LOGGER__ERROR("Udp recvfrom failed with timeout");
}
return HAILO_TIMEOUT;
} else if (EINTR == errno) {
LOGGER__ERROR("Udp recv interrupted!");
return HAILO_INTERRUPTED_BY_SIGNAL;
} else {
LOGGER__ERROR("Udp failed to recv data");
return HAILO_ETH_RECV_FAILURE;
}
}
else if ((0 == number_of_received_bytes) && (0 != dest_buffer_size)) {
LOGGER__INFO("Udp socket was aborted");
return HAILO_STREAM_INTERNAL_ABORT;
}
if (result_src_addr_size > src_addr_size) {
LOGGER__ERROR("src_addr size invalid");
return HAILO_ETH_RECV_FAILURE;
}
*bytes_received = (size_t)number_of_received_bytes;
return HAILO_SUCCESS;
}
hailo_status Socket::has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug)
{
hailo_status status = HAILO_UNINITIALIZED;
static const size_t DEST_BUFFER_SIZE = 1;
std::array<uint8_t, DEST_BUFFER_SIZE> dest_buffer{};
size_t number_of_received_bytes = 0;
status = recv_from(dest_buffer.data(), dest_buffer.size(), 0, src_addr, src_addr_size, &number_of_received_bytes, log_timeouts_in_debug);
if ((status == HAILO_TIMEOUT) && log_timeouts_in_debug) {
LOGGER__DEBUG("recv_from failed with timeout");
return HAILO_TIMEOUT;
} else {
CHECK_SUCCESS(status);
assert(number_of_received_bytes > 0);
}
return HAILO_SUCCESS;
}
} /* namespace hailort */

View File

@@ -0,0 +1,284 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file traffic_control.cpp
* @brief Traffic Control wrapper
**/
#include "traffic_control.hpp"
#include "common/process.hpp"
#include "common/ethernet_utils.hpp"
#include "common/utils.hpp"
#include "hailo/buffer.hpp"
#include "byte_order.h"
#include <sstream>
#include <thread>
namespace hailort
{
Expected<TrafficControlUtil> TrafficControlUtil::create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec)
{
auto interface_name = get_interface_name(ip);
CHECK_EXPECTED(interface_name, "get_interface_name failed with status {}", interface_name.status());
auto board_id = ip_to_board_id(ip);
CHECK_EXPECTED(board_id, "ip_to_board_id failed with status {}", board_id.status());
auto is_sudo_needed = check_is_sudo_needed();
CHECK_EXPECTED(is_sudo_needed, "check_is_sudo_needed failed with status {}", is_sudo_needed.status());
return TrafficControlUtil(ip, interface_name.release(), board_id.release(), port, port_to_port_id(port),
rate_bytes_per_sec, is_sudo_needed.release());
}
TrafficControlUtil::TrafficControlUtil(const std::string& board_address, const std::string& interface_name,
uint32_t board_id, uint16_t board_port, uint16_t port_id, uint32_t rate_bytes_per_sec, bool is_sudo_needed) :
m_board_address(board_address),
m_interface_name(interface_name),
m_board_id(board_id),
m_board_port(board_port),
m_port_id(port_id),
m_rate_bytes_per_sec(rate_bytes_per_sec),
m_is_sudo_needed(is_sudo_needed)
{}
hailo_status TrafficControlUtil::set_rate_limit()
{
LOGGER__INFO("Setting UDP rate to {} Byte/sec for {}:{}", m_rate_bytes_per_sec, m_board_address, m_board_port);
auto status = add_board_to_interface();
CHECK_SUCCESS(status, "add_board_to_interface failed with status {}", status);
status = add_input_to_inteface();
CHECK_SUCCESS(status, "add_input_to_inteface failed with status {}", status);
return status;
}
hailo_status TrafficControlUtil::reset_rate_limit()
{
LOGGER__INFO("Resetting UDP rate for {}:{}", m_board_address, m_board_port);
// Best effort
const auto del_input_status = del_input();
const auto del_board_status = del_board();
CHECK_SUCCESS(del_input_status);
CHECK_SUCCESS(del_board_status);
return HAILO_SUCCESS;
}
hailo_status TrafficControlUtil::add_board_to_interface()
{
CHECK_SUCCESS(tc_qdisc_add_dev(m_interface_name));
CHECK_SUCCESS(tc_class_add_dev_for_board(m_interface_name, m_board_id));
return HAILO_SUCCESS;
}
hailo_status TrafficControlUtil::add_input_to_inteface()
{
CHECK_SUCCESS(tc_class_add_dev_for_input(m_interface_name, m_board_id, m_port_id, m_rate_bytes_per_sec));
CHECK_SUCCESS(tc_filter_add_dev_for_input(m_interface_name, m_board_address, m_port_id, m_board_port));
return HAILO_SUCCESS;
}
hailo_status TrafficControlUtil::del_input()
{
static const auto SLEEP_BETWEEN_DELS = std::chrono::milliseconds(200);
CHECK_SUCCESS(tc_filter_del_dev_for_input(m_interface_name, m_board_address, m_port_id, m_board_port));
std::this_thread::sleep_for(SLEEP_BETWEEN_DELS);
CHECK_SUCCESS(tc_class_del_dev_for_input(m_interface_name, m_board_id, m_port_id));
return HAILO_SUCCESS;
}
hailo_status TrafficControlUtil::del_board()
{
return tc_class_del_dev_for_board(m_interface_name, m_board_id);
}
hailo_status TrafficControlUtil::tc_qdisc_add_dev(const std::string &interface_name)
{
// Right now we do not delete the qdisc itself on cleanup
// Hence, it's OK if the QDISC configuration already exists
// On 18.04 the error is a bit different so adding another allowed error
std::stringstream cmd;
cmd << "tc qdisc add dev " << interface_name << " root handle 1: htb default 10 direct_qlen 2000";
return run_command(cmd.str(), m_is_sudo_needed,
{"RTNETLINK answers: File exists", "Error: Exclusivity flag on, cannot modify."});
}
hailo_status TrafficControlUtil::tc_class_add_dev_for_board(const std::string &interface_name, uint32_t board_id)
{
std::stringstream cmd;
cmd << "tc class add dev " << interface_name << " parent 1: classid 1:" << board_id << " htb rate 1Gbit";
return run_command(cmd.str(), m_is_sudo_needed,
{"RTNETLINK answers: File exists", "Error: Exclusivity flag on, cannot modify."});
}
hailo_status TrafficControlUtil::tc_class_add_dev_for_input(const std::string &interface_name, uint32_t board_id,
uint16_t port_id, uint32_t rate_bytes_per_sec)
{
std::stringstream cmd;
cmd << "tc class add dev " << interface_name << " parent 1:" << board_id << " classid 1:" << port_id
<< " htb rate " << rate_bytes_per_sec << "bps ceil " << rate_bytes_per_sec << "bps maxburst 1kb";
return run_command(cmd.str(), m_is_sudo_needed);
}
hailo_status TrafficControlUtil::tc_filter_add_dev_for_input(const std::string &interface_name,
const std::string &board_ip, uint16_t port_id, uint16_t board_port)
{
std::stringstream cmd;
cmd << "tc filter add dev " << interface_name << " protocol ip parent 1:0 prio 1 u32 match ip dst " << board_ip
<< " match ip dport " << board_port << " 0xffff flowid 1:" << port_id;
return run_command(cmd.str(), m_is_sudo_needed);
}
hailo_status TrafficControlUtil::tc_filter_del_dev_for_input(const std::string &interface_name,
const std::string &board_ip, uint16_t port_id, uint16_t board_port)
{
std::stringstream cmd;
cmd << "tc filter del dev " << interface_name << " protocol ip parent 1:0 prio 1 u32 match ip dst "
<< board_ip << " match ip dport " << board_port << " 0xffff flowid 1:" << port_id;
return run_command(cmd.str(), m_is_sudo_needed, {}, true);
}
hailo_status TrafficControlUtil::tc_class_del_dev_for_input(const std::string &interface_name,
uint32_t board_id, uint16_t port_id)
{
std::stringstream cmd;
cmd << "tc class del dev " << interface_name << " parent 1:" << board_id << " classid 1:" << port_id;
return run_command(cmd.str(), m_is_sudo_needed, {}, true);
}
hailo_status TrafficControlUtil::tc_class_del_dev_for_board(const std::string &interface_name,
uint32_t board_id)
{
std::stringstream cmd;
cmd << "tc class del dev " << interface_name << " parent 1: classid 1:" << board_id;
return run_command(cmd.str(), m_is_sudo_needed, {}, true);
}
Expected<std::string> TrafficControlUtil::get_interface_name(const std::string &ip)
{
auto interface_name = Buffer::create(EthernetUtils::MAX_INTERFACE_SIZE, 0);
CHECK_EXPECTED(interface_name);
CHECK_SUCCESS_AS_EXPECTED(EthernetUtils::get_interface_from_board_ip(ip.c_str(),
interface_name->as_pointer<char>(), interface_name->size()));
return interface_name->to_string();
}
Expected<uint32_t> TrafficControlUtil::ip_to_board_id(const std::string &ip)
{
// Takes last digit from 3 octet + the whole 4th octet
// We try to get a unique id
const auto last_dot_loc = ip.find_last_of('.');
if (std::string::npos == last_dot_loc) {
LOGGER__ERROR("\".\" character not found in ip=\"{}\"", ip.c_str());
return make_unexpected(HAILO_INVALID_ARGUMENT);
}
std::string board_id_str = ip[last_dot_loc - 1] + ip.substr(last_dot_loc + 1);
uint32_t board_id = std::atoi(board_id_str.c_str());
if (0 == board_id) {
LOGGER__ERROR("atoi failed parsing \"{}\"", board_id_str.c_str());
return make_unexpected(HAILO_INVALID_ARGUMENT);
}
return board_id;
}
uint16_t TrafficControlUtil::port_to_port_id(uint16_t port)
{
// We use % to make the id unique (hopefully)
return port % 1000;
}
Expected<bool> TrafficControlUtil::check_is_sudo_needed()
{
const auto result = Process::create_and_wait_for_output("id -u", MAX_COMMAND_OUTPUT_LENGTH);
CHECK_EXPECTED(result);
// If the user id is zero then we don't need to add `sudo` to our commands
return std::move(result->second != "0");
}
hailo_status TrafficControlUtil::run_command(const std::string &commnad, bool add_sudo,
const std::vector<std::string> &allowed_errors, bool ignore_fails)
{
// Note: we redirect stderr to stdout
const auto result = Process::create_and_wait_for_output(
add_sudo ? "sudo " + commnad + " 2>&1" : commnad + " 2>&1",
MAX_COMMAND_OUTPUT_LENGTH);
CHECK_EXPECTED_AS_STATUS(result);
const uint32_t exit_code = result->first;
if (0 == exit_code) {
return HAILO_SUCCESS;
}
std::string cmd_output = result->second;
// No output = everything was OK
bool is_output_valid = cmd_output.empty();
if ((!is_output_valid) && (!allowed_errors.empty())) {
is_output_valid = (std::find(allowed_errors.cbegin(), allowed_errors.cend(), cmd_output) != allowed_errors.cend());
}
if (is_output_valid || ignore_fails) {
LOGGER__TRACE("Commnad \"{}\" returned a non-zero exit code ({});"
"ignoring (ignore_fails={}, is_output_valid={}).",
cmd_output.c_str(), exit_code, is_output_valid, ignore_fails);
return HAILO_SUCCESS;
}
LOGGER__ERROR("Commnad \"{}\" returned a non-zero exit code ({}), failing.", cmd_output.c_str(), exit_code);
return HAILO_TRAFFIC_CONTROL_FAILURE;
}
Expected<TrafficControl> TrafficControl::create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec)
{
auto tc_util = TrafficControlUtil::create(ip, port, rate_bytes_per_sec);
CHECK_EXPECTED(tc_util);
hailo_status rate_set_status = HAILO_UNINITIALIZED;
TrafficControl tc(tc_util.release(), rate_set_status);
CHECK_SUCCESS_AS_EXPECTED(rate_set_status, "Failed setting rate limit with status {}", rate_set_status);
return tc;
}
TrafficControl::~TrafficControl()
{
if (m_call_reset) {
const auto status = m_tc_util.reset_rate_limit();
if (HAILO_SUCCESS != status) {
LOGGER__ERROR("reset_rate_limit failed with status={}", status);
}
}
}
TrafficControl::TrafficControl(TrafficControl &&other) :
m_tc_util(std::move(other.m_tc_util)),
m_call_reset(std::exchange(other.m_call_reset, false))
{}
TrafficControl::TrafficControl(TrafficControlUtil &&tc, hailo_status &rate_set_status) :
m_tc_util(std::move(tc)),
m_call_reset(false)
{
rate_set_status = m_tc_util.reset_rate_limit();
if (HAILO_SUCCESS != rate_set_status) {
// This should succeed if there were previous TC rules set or if there weren't any
// Hence, we won't continue
LOGGER__ERROR("reset_rate_limit failed with status={}", rate_set_status);
return;
}
// We want to call reset in the dtor even if set_rate_limit fails
m_call_reset = true;
rate_set_status = m_tc_util.set_rate_limit();
if (HAILO_SUCCESS != rate_set_status) {
LOGGER__ERROR("set_rate_limit failed with status={}", rate_set_status);
return;
}
}
} /* namespace hailort */

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file traffic_control.hpp
* @brief Traffic Control wrapper
*
*
**/
#ifndef _TRAFFIC_CONTROL_HPP_
#define _TRAFFIC_CONTROL_HPP_
#include "common/utils.hpp"
#include "hailo/hailort.h"
#include "hailo/expected.hpp"
namespace hailort
{
// Contains helper functions to work with the Traffic control command line tool
class TrafficControlUtil final
{
public:
static Expected<TrafficControlUtil> create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec);
static Expected<std::string> get_interface_name(const std::string &ip);
~TrafficControlUtil() = default;
TrafficControlUtil(TrafficControlUtil&) = delete;
TrafficControlUtil &operator=(const TrafficControlUtil &) = delete;
TrafficControlUtil &operator=(TrafficControlUtil &&) = delete;
TrafficControlUtil(TrafficControlUtil &&other) = default;
hailo_status set_rate_limit();
hailo_status reset_rate_limit();
private:
TrafficControlUtil(const std::string& board_address, const std::string& interface_name,
uint32_t board_id, uint16_t board_port, uint16_t port_id, uint32_t rate_bytes_per_sec, bool is_sudo_needed);
hailo_status add_board_to_interface();
hailo_status add_input_to_inteface();
hailo_status del_input();
hailo_status del_board();
hailo_status tc_qdisc_add_dev(const std::string &interface_name);
hailo_status tc_class_add_dev_for_board(const std::string &interface_name, uint32_t board_id);
hailo_status tc_class_add_dev_for_input(const std::string &interface_name, uint32_t board_id,
uint16_t port_id, uint32_t rate_bytes_per_sec);
hailo_status tc_filter_add_dev_for_input(const std::string &interface_name, const std::string &board_ip,
uint16_t port_id, uint16_t board_port);
hailo_status tc_filter_del_dev_for_input(const std::string &interface_name, const std::string &board_ip,
uint16_t port_id, uint16_t board_port);
hailo_status tc_class_del_dev_for_input(const std::string &interface_name, uint32_t board_id,
uint16_t port_id);
hailo_status tc_class_del_dev_for_board(const std::string &interface_name, uint32_t board_id);
static const uint32_t MAX_COMMAND_OUTPUT_LENGTH = 100;
static Expected<std::string> get_interface_address(const struct in_addr *addr);
static Expected<uint32_t> ip_to_board_id(const std::string &ip);
static uint16_t port_to_port_id(uint16_t port);
static Expected<bool> check_is_sudo_needed();
static hailo_status run_command(const std::string &commnad, bool add_sudo,
const std::vector<std::string> &allowed_errors = {}, bool ignore_fails = false);
const std::string m_board_address;
const std::string m_interface_name;
const uint32_t m_board_id;
const uint16_t m_board_port;
const uint16_t m_port_id;
const uint32_t m_rate_bytes_per_sec;
const bool m_is_sudo_needed;
};
// RAII for TrafficControlUtil
class TrafficControl final
{
public:
static Expected<TrafficControl> create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec);
~TrafficControl();
TrafficControl(TrafficControl&) = delete;
TrafficControl &operator=(const TrafficControl &) = delete;
TrafficControl &operator=(TrafficControl &&) = delete;
TrafficControl(TrafficControl &&other);
private:
TrafficControl(TrafficControlUtil &&tc, hailo_status &rate_set_status);
TrafficControlUtil m_tc_util;
bool m_call_reset;
};
} /* namespace hailort */
#endif /* _TRAFFIC_CONTROL_HPP_ */

View File

@@ -0,0 +1,207 @@
#include "common/ethernet_utils.hpp"
#include "common/socket.hpp"
#include "common/os/windows/string_conversion.hpp"
#include "hailo/hailort.h"
#include "common/logger_macros.hpp"
#include "hailo/buffer.hpp"
#include <array>
#include <iphlpapi.h>
namespace hailort
{
NetworkInterface::NetworkInterface(uint32_t index, const std::string& name, const std::string& friendly_name, const std::string& ip) :
m_index(index),
m_name(name),
m_friendly_name(friendly_name),
m_ip(ip)
{}
uint32_t NetworkInterface::index() const
{
return m_index;
}
std::string NetworkInterface::name() const
{
return m_name;
}
std::string NetworkInterface::friendly_name() const
{
return m_friendly_name;
}
std::string NetworkInterface::ip() const
{
return m_ip;
}
Expected<NetworkInterfaces> NetworkInterface::get_all_interfaces()
{
static const ULONG IPV4 = AF_INET;
static const ULONG UNICAST_ONLY = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
static const PVOID RESERVED = nullptr;
static const PIP_ADAPTER_ADDRESSES NO_INTERFACE_INFO = nullptr;
ULONG required_size = 0;
ULONG ret_value = GetAdaptersAddresses(IPV4, UNICAST_ONLY, RESERVED, NO_INTERFACE_INFO, &required_size);
if (ret_value != ERROR_BUFFER_OVERFLOW) {
LOGGER__ERROR("Failed calculating necessary size for IP_ADAPTER_ADDRESSES. "
"Expected ret_value=ERROR_BUFFER_OVERFLOW, received={}", ret_value);
return make_unexpected(HAILO_UNEXPECTED_INTERFACE_INFO_FAILURE);
}
auto interface_info_buffer = Buffer::create(required_size, 0);
CHECK_EXPECTED(interface_info_buffer);
ret_value = GetAdaptersAddresses(IPV4, UNICAST_ONLY, RESERVED,
interface_info_buffer->as_pointer<IP_ADAPTER_ADDRESSES>(), &required_size);
if (ret_value == ERROR_NO_DATA) {
LOGGER__ERROR("No IPv4 interfaces found");
return make_unexpected(HAILO_NO_IPV4_INTERFACES_FOUND);
} else if (ret_value != NO_ERROR) {
LOGGER__ERROR("GetInterfaceInfo failed with error: {}", ret_value);
return make_unexpected(HAILO_UNEXPECTED_INTERFACE_INFO_FAILURE);
}
NetworkInterfaces interfaces;
PIP_ADAPTER_ADDRESSES interface_info = interface_info_buffer->as_pointer<IP_ADAPTER_ADDRESSES>();
while (interface_info != nullptr) {
PIP_ADAPTER_UNICAST_ADDRESS first_unicast_address = interface_info->FirstUnicastAddress;
// TODO: keep a vector of all addresses
if (first_unicast_address == nullptr) {
LOGGER__DEBUG("first_unicast_address is null. Skipping.");
continue;
}
const auto address_struct = first_unicast_address->Address.lpSockaddr;
if ((address_struct == nullptr) || (address_struct->sa_family != AF_INET)) {
LOGGER__DEBUG("Unicast address is invalid. Skipping.");
continue;
}
auto ip = Buffer::create(IPV4_STRING_MAX_LENGTH);
CHECK_EXPECTED(ip);
const auto result = Socket::ntop(AF_INET, &(reinterpret_cast<sockaddr_in *>(address_struct)->sin_addr),
ip->as_pointer<char>(), EthernetUtils::MAX_INTERFACE_SIZE);
if (result != HAILO_SUCCESS) {
LOGGER__DEBUG("Failed converting unicast address to string (result={}). Skipping.", result);
continue;
}
const auto friendly_name_ansi = StringConverter::utf16_to_ansi(interface_info->FriendlyName);
if (!friendly_name_ansi.has_value()) {
LOGGER__DEBUG("Failed converting the interface's friendly_name to ansi (result={}). Skipping.",
friendly_name_ansi.status());
continue;
}
interfaces.emplace_back(interface_info->IfIndex, interface_info->AdapterName,
friendly_name_ansi.value(), ip->to_string());
interface_info = interface_info->Next;
}
return interfaces;
}
ArpTable::ArpTable(const std::unordered_map<uint32_t, MacAddress>& table) :
m_table(table)
{}
Expected<MacAddress> ArpTable::get_mac_address(uint32_t ip) const
{
auto search = m_table.find(ip);
if (search == m_table.end()) {
return make_unexpected(HAILO_MAC_ADDRESS_NOT_FOUND);
}
return Expected<MacAddress>(m_table.at(ip));
}
Expected<ArpTable> ArpTable::create(uint32_t interface_index)
{
static const PMIB_IPNETTABLE NO_NETTABLE = nullptr;
static const BOOL SORTED = true;
ULONG required_size = 0;
ULONG ret_value = GetIpNetTable(NO_NETTABLE, &required_size, SORTED);
if (ret_value != ERROR_INSUFFICIENT_BUFFER) {
LOGGER__ERROR("Failed calculating necessary size for MIB_IPNETTABLE. Expected ret_value=ERROR_INSUFFICIENT_BUFFER, received={}", ret_value);
return make_unexpected(HAILO_UNEXPECTED_ARP_TABLE_FAILURE);
}
auto ip_net_table_buffer = Buffer::create(required_size, 0);
CHECK_EXPECTED(ip_net_table_buffer);
ret_value = GetIpNetTable(ip_net_table_buffer->as_pointer<MIB_IPNETTABLE>(), &required_size, SORTED);
if (ret_value == ERROR_NO_DATA) {
LOGGER__ERROR("No IPv4 interfaces found");
return make_unexpected(HAILO_NO_IPV4_INTERFACES_FOUND);
} else if (ret_value != NO_ERROR) {
LOGGER__ERROR("GetIpNetTable failed with error: {}", ret_value);
return make_unexpected(HAILO_UNEXPECTED_ARP_TABLE_FAILURE);
}
std::unordered_map<uint32_t, MacAddress> result;
const PMIB_IPNETTABLE ip_net_table = ip_net_table_buffer->as_pointer<MIB_IPNETTABLE>();
for (uint32_t i = 0; i < ip_net_table->dwNumEntries; i++) {
if (ip_net_table->table[i].dwIndex != interface_index) {
continue;
}
if (ip_net_table->table[i].dwPhysAddrLen != MacAddressSize) {
continue;
}
const uint32_t ip = ip_net_table->table[i].dwAddr;
MacAddress mac{};
memcpy(mac.data(), ip_net_table->table[i].bPhysAddr, MacAddressSize);
result[ip] = mac;
}
return result;
}
hailo_status EthernetUtils::get_interface_from_board_ip(const char *board_ip, char *interface_name, size_t interface_name_length)
{
CHECK_ARG_NOT_NULL(interface_name);
CHECK_ARG_NOT_NULL(board_ip);
auto network_interfaces = NetworkInterface::get_all_interfaces();
CHECK_EXPECTED_AS_STATUS(network_interfaces);
struct in_addr board_ip_struct{};
auto status = Socket::pton(AF_INET, board_ip, &board_ip_struct);
CHECK_SUCCESS(status);
for (const auto& network_interface : network_interfaces.value()) {
auto arp_table = ArpTable::create(network_interface.index());
CHECK_EXPECTED_AS_STATUS(arp_table);
const auto mac_address = arp_table->get_mac_address(static_cast<uint32_t>(board_ip_struct.S_un.S_addr));
if (mac_address) {
(void)strncpy(interface_name, network_interface.friendly_name().c_str(), interface_name_length);
return HAILO_SUCCESS;
}
}
return HAILO_ETH_INTERFACE_NOT_FOUND;
}
hailo_status EthernetUtils::get_ip_from_interface(const char *interface_name, char *ip, size_t ip_length)
{
CHECK_ARG_NOT_NULL(interface_name);
CHECK_ARG_NOT_NULL(ip);
auto network_interfaces = NetworkInterface::get_all_interfaces();
CHECK_EXPECTED_AS_STATUS(network_interfaces);
for (const auto& network_interface : network_interfaces.value()) {
if (network_interface.friendly_name() == interface_name) {
(void)strncpy(ip, network_interface.ip().c_str(), ip_length);
return HAILO_SUCCESS;
}
}
return HAILO_ETH_INTERFACE_NOT_FOUND;
}
} /* namespace hailort */

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file filesystem.cpp
* @brief Filesystem wrapper for Windows
**/
#include "common/filesystem.hpp"
#include "common/logger_macros.hpp"
#include "common/utils.hpp"
#include <shlwapi.h>
namespace hailort
{
const char *Filesystem::SEPARATOR = "\\";
Expected<Filesystem::FindFile> Filesystem::FindFile::create(const std::string &dir_path)
{
static const char *DIR_WALK_WILD_CARD = "*";
const std::string dir_path_with_sep = has_suffix(dir_path, SEPARATOR) ? dir_path + DIR_WALK_WILD_CARD :
dir_path + SEPARATOR + DIR_WALK_WILD_CARD;
WIN32_FIND_DATAA find_data{};
auto find_handle = FindFirstFileA(dir_path_with_sep.c_str(), &find_data);
if (INVALID_HANDLE_VALUE == find_handle) {
const auto last_error = GetLastError();
if (last_error == ERROR_FILE_NOT_FOUND) {
LOGGER__ERROR("No matching files could be found \"{}\"", dir_path_with_sep.c_str());
} else {
LOGGER__ERROR("FindFirstFileA(\"{}\") failed with LE={}", dir_path_with_sep.c_str(), last_error);
}
return make_unexpected(HAILO_FILE_OPERATION_FAILURE);
}
// Note: find_data will be copied into the m_find_data member (it doesn't contain pointers)
return std::move(FindFile(find_handle, find_data));
}
Filesystem::FindFile::FindFile(HANDLE find_hadle, const WIN32_FIND_DATAA &find_data) :
m_find_handle(find_hadle),
m_find_data(find_data)
{}
Filesystem::FindFile::~FindFile()
{
if (INVALID_HANDLE_VALUE != m_find_handle) {
const auto result = FindClose(m_find_handle);
if (0 == result) {
LOGGER__ERROR("FindClose on handle={} failed with LE={}", m_find_handle, GetLastError());
}
}
}
Filesystem::FindFile::FindFile(FindFile &&other) :
m_find_handle(std::exchange(other.m_find_handle, INVALID_HANDLE_VALUE)),
m_find_data(other.m_find_data)
{}
Filesystem::FileInfo Filesystem::FindFile::get_cur_file_info()
{
return {m_find_data.cFileName, m_find_data.dwFileAttributes};
}
hailo_status Filesystem::FindFile::next_file()
{
const auto result = FindNextFileA(m_find_handle, &m_find_data);
if (result) {
return HAILO_SUCCESS;
}
const auto last_error = GetLastError();
if (last_error == ERROR_NO_MORE_FILES) {
// No more files
return HAILO_INVALID_OPERATION;
}
LOGGER__ERROR("FindNextFileA() failed with LE={}", last_error);
return HAILO_FILE_OPERATION_FAILURE;
}
bool Filesystem::is_regular_or_readonly_file(DWORD attrs)
{
return (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0;
}
Expected<std::vector<std::string>> Filesystem::get_files_in_dir_flat(const std::string &dir_path)
{
const std::string dir_path_with_sep = has_suffix(dir_path, SEPARATOR) ? dir_path : dir_path + SEPARATOR;
auto dir = FindFile::create(dir_path_with_sep);
CHECK_EXPECTED(dir);
std::vector<std::string> files;
auto file_info = dir->get_cur_file_info();
if (is_regular_or_readonly_file(file_info.attrs)) {
files.emplace_back(file_info.path);
}
hailo_status status = HAILO_UNINITIALIZED;
while (true) {
status = dir->next_file();
if (HAILO_INVALID_OPERATION == status) {
// We're done
break;
}
if (HAILO_SUCCESS != status) {
// Best effort
LOGGER__ERROR("next_file failed with status {}; skipping", status);
continue;
}
file_info = dir->get_cur_file_info();
if (is_regular_or_readonly_file(file_info.attrs)) {
files.emplace_back(dir_path_with_sep + file_info.path);
}
}
return files;
}
Expected<bool> Filesystem::is_directory(const std::string &path)
{
if (path.length() > MAX_PATH) {
LOGGER__ERROR("path is too long (MAX_PATH={}, received length={}", MAX_PATH, path.length());
return make_unexpected(HAILO_INVALID_ARGUMENT);
}
return PathIsDirectoryA(path.c_str());
}
} /* namespace hailort */

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file process.cpp
* @brief Process wrapper for Windows
**/
#include "common/process.hpp"
#include "hailo/hailort.h"
#include "common/utils.hpp"
namespace hailort
{
Expected<std::pair<int32_t, std::string>> Process::create_and_wait_for_output(const std::string &command_line, uint32_t max_output_size)
{
// TODO: Add windows impl (HRT-2510)
command_line;
max_output_size;
return make_unexpected(HAILO_NOT_IMPLEMENTED);
}
} /* namespace hailort */

View File

@@ -0,0 +1,288 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file socket.cpp
* @brief Socket wrapper for Windows
**/
#include "common/socket.hpp"
#include <array>
namespace hailort
{
#define WSA_VERSION MAKEWORD(2, 2)
hailo_status Socket::SocketModuleWrapper::init_module()
{
uint16_t wsa_version = WSA_VERSION;
WSADATA wsa_data{};
int wsa_rt = SOCKET_ERROR;
wsa_rt = WSAStartup(wsa_version, &wsa_data);
CHECK(0 == wsa_rt, HAILO_ETH_FAILURE, "WSAStartup failed. rt={}", wsa_rt);
return HAILO_SUCCESS;
}
hailo_status Socket::SocketModuleWrapper::free_module()
{
int wsa_rt = SOCKET_ERROR;
wsa_rt = WSACleanup();
CHECK(0 == wsa_rt, HAILO_ETH_FAILURE, "WSACleanup failed. LE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
Expected<Socket> Socket::create(int af, int type, int protocol)
{
auto module_wrapper = SocketModuleWrapper::create();
CHECK_EXPECTED(module_wrapper);
auto socket_fd = create_socket_fd(af, type, protocol);
CHECK_EXPECTED(socket_fd);
auto obj = Socket(module_wrapper.release(), socket_fd.release());
return std::move(obj);
}
Socket::Socket(SocketModuleWrapper &&module_wrapper, const socket_t socket_fd) :
m_module_wrapper(std::move(module_wrapper)), m_socket_fd(socket_fd)
{
}
Socket::~Socket()
{
auto status = close_socket_fd();
if (HAILO_SUCCESS != status) {
LOGGER__ERROR("Failed to free socket fd with status {:X}");
}
}
Expected<socket_t> Socket::create_socket_fd(int af, int type, int protocol)
{
socket_t local_socket = INVALID_SOCKET;
local_socket = socket(af, type, protocol);
CHECK_VALID_SOCKET_AS_EXPECTED(local_socket);
return local_socket;
}
hailo_status Socket::abort()
{
return HAILO_NOT_IMPLEMENTED;
}
hailo_status Socket::close_socket_fd()
{
if (INVALID_SOCKET != m_socket_fd) {
int socket_rc = closesocket(m_socket_fd);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to close socket. WSALE={}", WSAGetLastError());
}
return HAILO_SUCCESS;
}
hailo_status Socket::socket_bind(const sockaddr *addr, socklen_t len)
{
int socket_rc = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(addr);
socket_rc = bind(m_socket_fd, addr, len);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to bind socket. WSALE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
hailo_status Socket::get_sock_name(sockaddr *addr, socklen_t *len)
{
int socket_rc = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(addr);
CHECK_ARG_NOT_NULL(len);
socket_rc = getsockname(m_socket_fd, addr, len);
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed getsockname. WSALE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
hailo_status Socket::ntop(int af, const void *src, char *dst, socklen_t size)
{
CHECK_ARG_NOT_NULL(src);
CHECK_ARG_NOT_NULL(dst);
auto module_wrapper = SocketModuleWrapper::create();
CHECK_EXPECTED_AS_STATUS(module_wrapper);
const char *inet_result = inet_ntop(af, src, dst, size);
CHECK(nullptr != inet_result, HAILO_ETH_FAILURE, "Failed inet_ntop. WSALE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
hailo_status Socket::pton(int af, const char *src, void *dst)
{
int inet_result = SOCKET_ERROR;
CHECK_ARG_NOT_NULL(src);
CHECK_ARG_NOT_NULL(dst);
auto module_wrapper = SocketModuleWrapper::create();
CHECK_EXPECTED_AS_STATUS(module_wrapper);
inet_result = inet_pton(af, src, dst);
CHECK(1 == inet_result, HAILO_ETH_FAILURE, "Failed inet_pton. WSALE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
hailo_status Socket::set_recv_buffer_size_max()
{
int socket_rc = SOCKET_ERROR;
// TOOD: MAX_SIZE?? https://docs.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options
const int MAX_RECV_BUFFER_SIZE = 52428800;
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVBUF,
reinterpret_cast<const char*>(&MAX_RECV_BUFFER_SIZE), sizeof(MAX_RECV_BUFFER_SIZE));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_RCVBUF). WSALE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
hailo_status Socket::set_timeout(const std::chrono::milliseconds timeout_ms, timeval_t *timeout)
{
int socket_rc = SOCKET_ERROR;
auto timeout_value = static_cast<uint32_t>(timeout_ms.count());
/* Validate arguments */
CHECK_ARG_NOT_NULL(timeout);
// From https://docs.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options (SO_RCVTIMEO):
// If the socket is created using the WSASocket function, then the dwFlags parameter must have the
// WSA_FLAG_OVERLAPPED attribute set for the timeout to function properly. Otherwise the timeout never takes effect.
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*>(&timeout_value), sizeof(timeout_value));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_RCVTIMEO). WSALE={}", WSAGetLastError());
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char*>(&timeout_value), sizeof(timeout_value));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_SNDTIMEO). WSALE={}", WSAGetLastError());
timeout->tv_sec = timeout_value / MILLISECONDS_IN_SECOND;
timeout->tv_usec = (timeout_value % MILLISECONDS_IN_SECOND) * MICROSECONDS_IN_MILLISECOND;
return HAILO_SUCCESS;
}
hailo_status Socket::enable_broadcast()
{
int socket_rc = SOCKET_ERROR;
int enable_broadcast = 1;
socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_BROADCAST,
reinterpret_cast<const char*>(&enable_broadcast), sizeof(enable_broadcast));
CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_BROADCAST). WSALE={}", WSAGetLastError());
return HAILO_SUCCESS;
}
hailo_status Socket::send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent)
{
int number_of_sent_bytes = SOCKET_ERROR;
/* Validate arguments */
CHECK_ARG_NOT_NULL(src_buffer);
CHECK_ARG_NOT_NULL(dest_addr);
CHECK_ARG_NOT_NULL(bytes_sent);
number_of_sent_bytes = sendto(m_socket_fd, reinterpret_cast<const char*>(src_buffer),
static_cast<int>(src_buffer_size), flags, dest_addr, dest_addr_size);
if (SOCKET_ERROR == number_of_sent_bytes) {
const int wsale = WSAGetLastError();
if (WSAETIMEDOUT == errno) {
LOGGER__ERROR("Udp send timeout");
return HAILO_TIMEOUT;
} else {
LOGGER__ERROR("Udp failed to send data, WSALE={}.", wsale);
return HAILO_ETH_SEND_FAILURE;
}
}
*bytes_sent = (size_t)number_of_sent_bytes;
return HAILO_SUCCESS;
}
hailo_status Socket::recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_recieved, bool log_timeouts_in_debug)
{
int number_of_received_bytes = SOCKET_ERROR;
socklen_t result_src_addr_size = src_addr_size;
/* Validate arguments */
CHECK_ARG_NOT_NULL(dest_buffer);
CHECK_ARG_NOT_NULL(src_addr);
CHECK_ARG_NOT_NULL(bytes_recieved);
number_of_received_bytes = recvfrom(m_socket_fd, reinterpret_cast<char*>(dest_buffer),
static_cast<int>(dest_buffer_size), flags, src_addr, &result_src_addr_size);
if (SOCKET_ERROR == number_of_received_bytes) {
const int wsale = WSAGetLastError();
if (WSAETIMEDOUT == wsale) {
if (log_timeouts_in_debug) {
LOGGER__DEBUG("Udp recvfrom failed with timeout");
} else {
LOGGER__ERROR("Udp recvfrom failed with timeout");
}
return HAILO_TIMEOUT;
} else {
LOGGER__ERROR("Udp failed to recv data. WSALE={}.", wsale);
return HAILO_ETH_RECV_FAILURE;
}
}
*bytes_recieved = static_cast<size_t>(number_of_received_bytes);
return HAILO_SUCCESS;
}
hailo_status Socket::has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug)
{
int number_of_received_bytes = SOCKET_ERROR;
socklen_t result_src_addr_size = src_addr_size;
static const size_t DEST_BUFFER_SIZE = 1;
std::array<char, DEST_BUFFER_SIZE> dest_buffer{};
/* Validate arguments */
CHECK_ARG_NOT_NULL(src_addr);
static const int NO_FLAGS = 0;
number_of_received_bytes = recvfrom(m_socket_fd, dest_buffer.data(), static_cast<int>(dest_buffer.size()), NO_FLAGS,
src_addr, &result_src_addr_size);
if (SOCKET_ERROR == number_of_received_bytes) {
const int wsale = WSAGetLastError();
if (WSAETIMEDOUT == wsale) {
if (log_timeouts_in_debug) {
LOGGER__DEBUG("Udp recvfrom failed with timeout");
} else {
LOGGER__ERROR("Udp recvfrom failed with timeout");
}
return HAILO_TIMEOUT;
}
// The message may be bigger than DEST_BUFFER_SIZE bytes, leading to WSAEMSGSIZE. This is ok
if (WSAEMSGSIZE != wsale) {
LOGGER__ERROR("Udp failed to recv data. WSALE={}.", wsale);
return HAILO_ETH_RECV_FAILURE;
}
}
return HAILO_SUCCESS;
}
} /* namespace hailort */

View File

@@ -0,0 +1,63 @@
#include <vector>
#include "common/os/windows/string_conversion.hpp"
#include "common/utils.hpp"
namespace hailort
{
Expected<std::wstring> StringConverter::ansi_to_utf16(const std::string& ansi_string)
{
static const UINT ANSI_CODE_PAGE = CP_ACP;
static const DWORD FAIL_ON_INVALID_CHARS = MB_ERR_INVALID_CHARS;
static const int CALCULATE_INPUT_LENGTH = -1;
static const LPWSTR NO_WIDE_CHAR_BUFFER = nullptr;
static const int CALCULATE_RESULT_LENGTH = 0;
const int required_length = MultiByteToWideChar(ANSI_CODE_PAGE, FAIL_ON_INVALID_CHARS, ansi_string.c_str(),
CALCULATE_INPUT_LENGTH, NO_WIDE_CHAR_BUFFER, CALCULATE_RESULT_LENGTH);
if (0 == required_length) {
LOGGER__ERROR("Failed calculating necessary length for '{}' as unicode string. LE={}", ansi_string, GetLastError());
return make_unexpected(HAILO_ANSI_TO_UTF16_CONVERSION_FAILED );
}
std::vector<wchar_t> result_buffer(required_length, L'\0');
const int allocated_length = MultiByteToWideChar(ANSI_CODE_PAGE, FAIL_ON_INVALID_CHARS, ansi_string.c_str(),
CALCULATE_INPUT_LENGTH, result_buffer.data(), required_length);
if (0 == allocated_length || allocated_length != required_length) {
LOGGER__ERROR("Failed converting '{}' to unicode string. LE={}", ansi_string, GetLastError());
return make_unexpected(HAILO_ANSI_TO_UTF16_CONVERSION_FAILED );
}
// result_buffer includes the terminating null
return std::wstring(result_buffer.data());
}
Expected<std::string> StringConverter::utf16_to_ansi(const std::wstring& utf16_string)
{
static const UINT ANSI_CODE_PAGE = CP_ACP;
static const DWORD NO_FLAGS = 0;
static const int CALCULATE_INPUT_LENGTH = -1;
static const LPSTR NO_UTF8_BUFFER = nullptr;
static const int CALCULATE_RESULT_LENGTH = 0;
static const LPCCH USE_SYSTEM_DEFAULT_CHAR = nullptr;
static const LPBOOL NO_CUSTOM_DEFAULT_CHAR = nullptr;
const int required_length = WideCharToMultiByte(ANSI_CODE_PAGE, NO_FLAGS, utf16_string.c_str(),
CALCULATE_INPUT_LENGTH, NO_UTF8_BUFFER, CALCULATE_RESULT_LENGTH, USE_SYSTEM_DEFAULT_CHAR, NO_CUSTOM_DEFAULT_CHAR);
if (0 == required_length) {
LOGGER__ERROR("Failed calculating necessary length for utf16_string as ansi string. LE={}", GetLastError());
return make_unexpected(HAILO_UTF16_TO_ANSI_CONVERSION_FAILED);
}
std::vector<char> result_buffer(required_length, '\0');
const int allocated_length = WideCharToMultiByte(ANSI_CODE_PAGE, NO_FLAGS, utf16_string.c_str(),
CALCULATE_INPUT_LENGTH, result_buffer.data(), required_length, USE_SYSTEM_DEFAULT_CHAR, NO_CUSTOM_DEFAULT_CHAR);
if (0 == allocated_length || allocated_length != required_length) {
LOGGER__ERROR("Failed converting utf16_string to ansi string. LE={}", GetLastError());
return make_unexpected(HAILO_UTF16_TO_ANSI_CONVERSION_FAILED);
}
// result_buffer includes the terminating null
return std::string(result_buffer.data());
}
} /* namespace hailort */

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file string_conversion.hpp
* @brief Safe string encoding conversions.
**/
#ifndef _OS_STRING_CONVERSION_HPP_
#define _OS_STRING_CONVERSION_HPP_
#include <string>
#include <hailo/platform.h>
#include <hailo/hailort.h>
#include "hailo/expected.hpp"
namespace hailort
{
class StringConverter final
{
public:
StringConverter() = delete;
static Expected<std::wstring> ansi_to_utf16(const std::string& ansi_string);
static Expected<std::string> utf16_to_ansi(const std::wstring& utf16_string);
};
} /* namespace hailort */
#endif /* _OS_STRING_CONVERSION_HPP_ */

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file process.hpp
* @brief Create shell processes and retrieve output
**/
#ifndef _OS_PROCESS_HPP_
#define _OS_PROCESS_HPP_
#include "hailo/hailort.h"
#include "hailo/platform.h"
#include "hailo/expected.hpp"
#include <string>
namespace hailort
{
class Process final {
public:
// Note:
// * This function will block!
// * If the process' output size exceeds max_output_size, the output will be truncated to max_output_size
// * We remove the trailing newline if it's the last char
static Expected<std::pair<int32_t, std::string>> create_and_wait_for_output(const std::string &command_line, uint32_t max_output_size);
Process() = delete;
private:
#if defined(__GNUC__)
class PopenWrapper final {
public:
static Expected<PopenWrapper> create(const std::string &command_line);
~PopenWrapper();
PopenWrapper(const PopenWrapper &other) = delete;
PopenWrapper &operator=(const PopenWrapper &other) = delete;
PopenWrapper &operator=(PopenWrapper &&other) = delete;
PopenWrapper(PopenWrapper &&other);
Expected<std::string> read_stdout(uint32_t max_output_size);
// This function is to be called only once!
int32_t close();
private:
PopenWrapper(const std::string &command_line, hailo_status &status);
const std::string m_command_line;
FILE* m_pipe;
};
#endif
};
} /* namespace hailort */
#endif /* _OS_PROCESS_HPP_ */

View File

@@ -0,0 +1,217 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file runtime_statistics_internal.hpp
* @brief Implementation of Accumulator<T> interface
**/
#ifndef _HAILO_RUNTIME_STATISTICS_INTERNAL_HPP_
#define _HAILO_RUNTIME_STATISTICS_INTERNAL_HPP_
#include "hailo/runtime_statistics.hpp"
#include <cmath>
#include <mutex>
#include <limits>
namespace hailort
{
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
class FullAccumulator : public Accumulator<T>
{
public:
// Creation isn't thread safe
FullAccumulator(const std::string& data_type) :
Accumulator<T>(data_type),
m_lock(),
m_count(0),
m_min(static_cast<double>(std::numeric_limits<T>::max())),
m_max(static_cast<double>(std::numeric_limits<T>::min())),
m_mean(0),
m_M2(0)
{}
FullAccumulator(FullAccumulator &&) = default;
FullAccumulator(const FullAccumulator &) = delete;
FullAccumulator &operator=(FullAccumulator &&) = delete;
FullAccumulator &operator=(const FullAccumulator &) = delete;
virtual ~FullAccumulator() = default;
virtual void add_data_point(T data) override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
m_min = std::min(m_min, static_cast<double>(data));
m_max = std::max(m_max, static_cast<double>(data));
m_count++;
// mean, variance, sd and mean_sd are calculated using Welford's_online_algorithm.
// See: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
const auto delta = static_cast<double>(data) - m_mean;
m_mean += delta / static_cast<double>(m_count);
m_M2 += delta * (static_cast<double>(data) - m_mean);
}
virtual AccumulatorResults get() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
return AccumulatorResults(count(), min(), max(), mean(), var(), sd(), mean_sd());
}
virtual AccumulatorResults get_and_clear() override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
auto result = get();
clear();
return result;
}
virtual Expected<size_t> count() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
return Expected<size_t>(m_count);
}
virtual Expected<double> min() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
if (m_count < 1) {
return make_unexpected(HAILO_UNINITIALIZED);
}
return Expected<double>(m_min);
}
virtual Expected<double> max() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
if (m_count < 1) {
return make_unexpected(HAILO_UNINITIALIZED);
}
return Expected<double>(m_max);
}
virtual Expected<double> mean() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
if (m_count < 1) {
// Otherwise we'll divide by zero
return make_unexpected(HAILO_UNINITIALIZED);
}
return Expected<double>(m_mean);
}
// Sample variance
virtual Expected<double> var() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
if (m_count < 2) {
// Otherwise we'll divide by zero
return make_unexpected(HAILO_UNINITIALIZED);
}
return Expected<double>(var_impl());
}
// Sample sd
virtual Expected<double> sd() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
if (m_count < 2) {
// Otherwise we'll divide by zero
return make_unexpected(HAILO_UNINITIALIZED);
}
return Expected<double>(sd_impl());
}
// Sample mean sd
virtual Expected<double> mean_sd() const override
{
std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
if (m_count < 2) {
// Otherwise we'll divide by zero
return make_unexpected(HAILO_UNINITIALIZED);
}
// Calculation based on: https://en.wikipedia.org/wiki/Standard_deviation#Standard_deviation_of_the_mean
return Expected<double>(sd_impl() / std::sqrt(m_count));
}
protected:
mutable std::recursive_mutex m_lock;
size_t m_count;
double m_min;
double m_max;
double m_mean;
double m_M2; // Sum {i=1...n} (x_i-x_mean)^2
// These functions are to be called after acquiring the mutex
virtual void clear()
{
m_count = 0;
m_min = static_cast<double>(std::numeric_limits<T>::max());
m_max = static_cast<double>(std::numeric_limits<T>::min());
m_mean = 0;
m_M2 = 0;
}
double var_impl() const
{
return m_M2 / static_cast<double>(m_count - 1);
}
double sd_impl() const
{
return std::sqrt(var_impl());
}
};
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
class AverageFPSAccumulator : public FullAccumulator<T>
{
public:
// Creation isn't thread safe
AverageFPSAccumulator(const std::string& data_type) :
FullAccumulator<T>(data_type),
m_sum(0)
{}
AverageFPSAccumulator(AverageFPSAccumulator &&) = default;
AverageFPSAccumulator(const AverageFPSAccumulator &) = delete;
AverageFPSAccumulator &operator=(AverageFPSAccumulator &&) = delete;
AverageFPSAccumulator &operator=(const AverageFPSAccumulator &) = delete;
virtual ~AverageFPSAccumulator() = default;
// data is a duration of time.
// However, the statistics collected will be in frames per seconds (i.e. time^-1).
virtual void add_data_point(T data) override
{
assert(0 != data);
std::lock_guard<std::recursive_mutex> lock_guard(this->m_lock);
m_sum += data;
const double data_inverse = 1.0 / static_cast<double>(data);
// Note: 'this' is needed to access protected members of a template base class
this->m_min = std::min(this->m_min, data_inverse);
this->m_max = std::max(this->m_max, data_inverse);
this->m_count++;
// mean, variance, sd and mean_sd are calculated using Welford's_online_algorithm.
// See: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
const auto delta = data_inverse - this->m_mean;
// We calculate the arithmatic mean
this->m_mean = static_cast<double>(this->m_count) / static_cast<double>(m_sum);
this->m_M2 += delta * (data_inverse - this->m_mean);
}
virtual void clear() override
{
m_sum = 0;
FullAccumulator<T>::clear();
}
private:
T m_sum; // the sum of data added with add_data_point, before inversion
};
} /* namespace hailort */
#endif /* _HAILO_RUNTIME_STATISTICS_INTERNAL_HPP_ */

108
hailort/common/socket.hpp Normal file
View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file socket.hpp
* @brief TODO
**/
#ifndef __OS_SOCKET_H__
#define __OS_SOCKET_H__
#include <hailo/platform.h>
#include <hailo/hailort.h>
#include "common/utils.hpp"
#include "hailo/expected.hpp"
namespace hailort
{
// 12 for the octets (3 * 4, each octet<=255)
// 3 for the dots (".")
// 1 for the terminating null
#define IPV4_STRING_MAX_LENGTH (16)
#define PADDING_BYTES_SIZE (6)
#define PADDING_ALIGN_BYTES (8 - PADDING_BYTES_SIZE)
#define MIN_UDP_PAYLOAD_SIZE (24)
#define MAX_UDP_PAYLOAD_SIZE (1456)
#define MAX_UDP_PADDED_PAYLOAD_SIZE (MAX_UDP_PAYLOAD_SIZE - PADDING_BYTES_SIZE - PADDING_ALIGN_BYTES)
#define CHECK_VALID_SOCKET_AS_EXPECTED(sock) CHECK((sock) != INVALID_SOCKET, make_unexpected(HAILO_ETH_FAILURE), "Invalid socket")
class Socket final {
public:
static Expected<Socket> create(int af, int type, int protocol);
~Socket();
Socket(const Socket &other) = delete;
Socket &operator=(const Socket &other) = delete;
Socket &operator=(Socket &&other) = delete;
Socket(Socket &&other) noexcept :
m_module_wrapper(std::move(other.m_module_wrapper)), m_socket_fd(std::exchange(other.m_socket_fd, INVALID_SOCKET))
{};
static hailo_status ntop(int af, const void *src, char *dst, socklen_t size);
static hailo_status pton(int af, const char *src, void *dst);
hailo_status socket_bind(const sockaddr *addr, socklen_t len);
hailo_status get_sock_name(sockaddr *addr, socklen_t *len);
hailo_status set_recv_buffer_size_max();
hailo_status set_timeout(const std::chrono::milliseconds timeout_ms, timeval_t *timeout);
hailo_status enable_broadcast();
hailo_status abort();
// TODO: Should these be in udp.cpp?
// TODO: Work with const Buffer& instead of uint8_t*
hailo_status send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent);
hailo_status recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_received, bool log_timeouts_in_debug = false);
hailo_status has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug = false);
private:
class SocketModuleWrapper final {
public:
static Expected<SocketModuleWrapper> create()
{
auto status = HAILO_UNINITIALIZED;
auto obj = SocketModuleWrapper(status);
CHECK_SUCCESS_AS_EXPECTED(status);
return obj;
}
SocketModuleWrapper(hailo_status &status)
{
status = init_module();
}
SocketModuleWrapper(const SocketModuleWrapper &other) = delete;
SocketModuleWrapper &operator=(const SocketModuleWrapper &other) = delete;
SocketModuleWrapper &operator=(SocketModuleWrapper &&other) = delete;
SocketModuleWrapper(SocketModuleWrapper &&other) noexcept = default;
~SocketModuleWrapper()
{
auto status = free_module();
if (HAILO_SUCCESS != status) {
LOGGER__ERROR("Failed to free socket module.");
}
}
private:
static hailo_status init_module();
static hailo_status free_module();
};
Socket(SocketModuleWrapper &&module_wrapper, const socket_t socket_fd);
static Expected<socket_t> create_socket_fd(int af, int type, int protocol);
hailo_status close_socket_fd();
// Itialization dependency
SocketModuleWrapper m_module_wrapper;
socket_t m_socket_fd;
};
} /* namespace hailort */
#endif /* __OS_SOCKET_H__ */

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file string_utils.cpp
* @brief Utilities for string
**/
#include "common/string_utils.hpp"
#include "common/utils.hpp"
#include <stdlib.h>
#include <errno.h>
namespace hailort
{
// TODO: make it templated
Expected<uint32_t> StringUtils::to_uint32(const std::string &str, int base)
{
errno = 0;
char *end_pointer = nullptr;
auto value = strtoul(str.c_str(), &end_pointer, base);
static_assert(sizeof(value) >= sizeof(uint32_t), "Size of value must be equal or greater than size of uint32_t");
CHECK_AS_EXPECTED(errno == 0, HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint32_t. strtoul failed with errno {}", str, errno);
CHECK_AS_EXPECTED(((*end_pointer == '\0') || (*end_pointer == '\n') || (*end_pointer == ' ') || (*end_pointer == '\r')),
HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint32_t. strtoul failed with errno {}", str, errno);
if ((value == 0) && (end_pointer == str)) {
LOGGER__ERROR("Failed to convert string {} to uint32_t.", str);
return make_unexpected(HAILO_INVALID_ARGUMENT);
}
CHECK_AS_EXPECTED(((value >= std::numeric_limits<uint32_t>::min()) && (value <= std::numeric_limits<uint32_t>::max())),
HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint32_t.", str);
return static_cast<uint32_t>(value);
}
// TODO: make it templated
Expected<int32_t> StringUtils::to_int32(const std::string &str, int base)
{
errno = 0;
char *end_pointer = nullptr;
auto value = strtol(str.c_str(), &end_pointer, base);
static_assert(sizeof(value) >= sizeof(int32_t), "Size of value must be equal or greater than size of int32_t");
CHECK_AS_EXPECTED(errno == 0, HAILO_INVALID_ARGUMENT, "Failed to convert string {} to int32_t. strtol failed with errno {}", str, errno);
CHECK_AS_EXPECTED(((*end_pointer == '\0') || (*end_pointer == '\n') || (*end_pointer == ' ') || (*end_pointer == '\r')),
HAILO_INVALID_ARGUMENT, "Failed to convert string {} to int32_t. strtoul failed with errno {}", str, errno);
if ((value == 0) && (end_pointer == str)) {
LOGGER__ERROR("Failed to convert string {} to int32_t.", str);
return make_unexpected(HAILO_INVALID_ARGUMENT);
}
CHECK_AS_EXPECTED(((value >= std::numeric_limits<int32_t>::min()) && (value <= std::numeric_limits<int32_t>::max())),
HAILO_INVALID_ARGUMENT, "Failed to convert string {} to int32.", str);
return static_cast<int32_t>(value);
}
Expected<uint8_t> StringUtils::to_uint8(const std::string &str, int base)
{
auto number = to_uint32(str, base);
CHECK_EXPECTED(number);
CHECK_AS_EXPECTED(((number.value() >= std::numeric_limits<uint8_t>::min()) && (number.value() <= std::numeric_limits<uint8_t>::max())),
HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint8_t.", str);
return static_cast<uint8_t>(number.value());
}
} /* namespace hailort */

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file string_utils.hpp
* @brief Defines utilities methods for string.
**/
#ifndef _HAILO_STRING_UTILS_HPP_
#define _HAILO_STRING_UTILS_HPP_
#include "hailo/expected.hpp"
#include <string>
namespace hailort
{
class StringUtils {
public:
static Expected<int32_t> to_int32(const std::string &str, int base);
static Expected<uint8_t> to_uint8(const std::string &str, int base);
static Expected<uint32_t> to_uint32(const std::string &str, int base);
};
} /* namespace hailort */
#endif /* _HAILO_STRING_UTILS_HPP_ */

246
hailort/common/utils.hpp Normal file
View File

@@ -0,0 +1,246 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file utils.hpp
* @brief TODO: brief
*
* TODO: doc
**/
#ifndef HAILO_UTILS_H_
#define HAILO_UTILS_H_
#include <assert.h>
#include <hailo/hailort.h>
#include "common/logger_macros.hpp"
#include <spdlog/fmt/bundled/core.h>
#include <map>
#include <set>
namespace hailort
{
#define IS_FIT_IN_UINT8(number) ((std::numeric_limits<uint8_t>::max() >= ((int32_t)(number))) && (std::numeric_limits<uint8_t>::min() <= ((int32_t)(number))))
#define IS_FIT_IN_UINT16(number) ((std::numeric_limits<uint16_t>::max() >= ((int32_t)(number))) && (std::numeric_limits<uint16_t>::min() <= ((int32_t)(number))))
#define IS_FIT_IN_UINT16(number) ((std::numeric_limits<uint16_t>::max() >= ((int32_t)(number))) && (std::numeric_limits<uint16_t>::min() <= ((int32_t)(number))))
template <typename T>
static inline bool contains(const std::vector<T> &container, const T &value)
{
return std::find(container.begin(), container.end(), value) != container.end();
}
template <typename T, typename Q>
static inline bool contains(const std::map<Q, T> &container, Q value)
{
return (container.find(value) != container.end());
}
template <typename T, typename Q>
static inline bool contains(const std::unordered_map<Q, T> &container, Q value)
{
return (container.find(value) != container.end());
}
template <typename T>
static inline bool contains(const std::set<T> &container, T value)
{
return (container.find(value) != container.end());
}
// From https://stackoverflow.com/questions/57092289/do-stdmake-shared-and-stdmake-unique-have-a-nothrow-version
template <class T, class... Args>
static inline std::unique_ptr<T> make_unique_nothrow(Args&&... args)
noexcept(noexcept(T(std::forward<Args>(args)...)))
{
#ifndef NDEBUG
auto ptr = std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
if (nullptr == ptr) {
LOGGER__ERROR("make_unique failed, pointer is null!");
}
return ptr;
#else
return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
#endif
}
template <class T, class... Args>
static inline std::shared_ptr<T> make_shared_nothrow(Args&&... args)
noexcept(noexcept(T(std::forward<Args>(args)...)))
{
#ifndef NDEBUG
auto ptr = std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
if (nullptr == ptr) {
LOGGER__ERROR("make_shared failed, pointer is null!");
}
return ptr;
#else
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
#endif
}
#define ASSERT assert
#define ARRAY_ENTRIES(x) (sizeof(x) / sizeof((x)[0]))
#define RETURN_IF_ARG_NULL(arg) \
do { \
if (NULL == (arg)) { \
LOGGER__ERROR("Invalid argument: "#arg); \
return HAILO_INVALID_ARGUMENT; \
} \
} while(0)
#define _FREE(var, invalid_value, func) \
do { \
if ((invalid_value) != (var)) { \
free(var); \
var = (invalid_value); \
} \
} while(0)
#define FREE(p) _FREE(p, NULL, free)
#define _CLOSE(var, invalid_var_value, func, invalid_func_result, status) \
do { \
if ((invalid_var_value) != (var)) { \
if ((invalid_func_result) == func(var)) { \
LOGGER__ERROR("CLOSE failed"); \
if (HAILO_SUCCESS == (status)) { \
status = HAILO_CLOSE_FAILURE; \
} \
else { \
LOGGER__ERROR("Not setting status to HAILO_CLOSE_FAILURE since it is not HAILO_SUCCESS"); \
} \
} \
var = (invalid_var_value); \
} \
} while(0)
// TODO: Add tests in tests/utils/main.cpp
#define CLOSE(fd, status) _CLOSE(fd, -1, close, -1, status)
#define FCLOSE(file, status) _CLOSE(file, NULL, fclose, 0, status)
// Detect empty macro arguments
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define _TRIGGER_PARENTHESIS_(...) ,
#define ISEMPTY(...) \
_ISEMPTY( \
/* test if there is just one argument, eventually an empty \
one */ \
HAS_COMMA(__VA_ARGS__), \
/* test if _TRIGGER_PARENTHESIS_ together with the argument \
adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
/* test if the argument together with a parenthesis \
adds a comma */ \
HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
/* test if placing it between _TRIGGER_PARENTHESIS_ and the \
parenthesis adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \
)
#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,
//
#define __CONSTRUCT_MSG_1(dft_fmt, usr_fmt, ...) dft_fmt, ##__VA_ARGS__
#define __CONSTRUCT_MSG_0(dft_fmt, usr_fmt, ...) dft_fmt " - " usr_fmt, ##__VA_ARGS__
#define __CONSTRUCT_MSG(is_dft, dft_fmt, usr_fmt, ...) __CONSTRUCT_MSG_##is_dft(dft_fmt, usr_fmt, ##__VA_ARGS__)
#define _CONSTRUCT_MSG(is_dft, dft_fmt, usr_fmt, ...) __CONSTRUCT_MSG(is_dft, dft_fmt, usr_fmt, ##__VA_ARGS__)
#define CONSTRUCT_MSG(dft_fmt, ...) _CONSTRUCT_MSG(ISEMPTY(__VA_ARGS__), dft_fmt, "" __VA_ARGS__)
#define _CHECK(cond, ret_val, ...) \
do { \
if (!(cond)) { \
LOGGER__ERROR(__VA_ARGS__); \
return (ret_val); \
} \
} while(0)
/** Returns ret_val when cond is false */
#define CHECK(cond, ret_val, ...) _CHECK((cond), (ret_val), CONSTRUCT_MSG("CHECK failed", ##__VA_ARGS__))
#define CHECK_AS_EXPECTED(cond, ret_val, ...) \
_CHECK((cond), (make_unexpected(ret_val)), CONSTRUCT_MSG("CHECK_AS_EXPECTED failed", ##__VA_ARGS__))
#define CHECK_ARG_NOT_NULL(arg) _CHECK(nullptr != (arg), HAILO_INVALID_ARGUMENT, "CHECK_ARG_NOT_NULL for {} failed", #arg)
#define CHECK_ARG_NOT_NULL_AS_EXPECTED(arg) _CHECK(nullptr != (arg), make_unexpected(HAILO_INVALID_ARGUMENT), "CHECK_ARG_NOT_NULL_AS_EXPECTED for {} failed", #arg)
#define CHECK_NOT_NULL(arg, status) _CHECK(nullptr != (arg), status, "CHECK_NOT_NULL for {} failed", #arg)
#define CHECK_NOT_NULL_AS_EXPECTED(arg, status) _CHECK(nullptr != (arg), make_unexpected(status), "CHECK_NOT_NULL_AS_EXPECTED for {} failed", #arg)
#define _CHECK_SUCCESS(status, is_default, fmt, ...) \
do { \
const auto &__check_success_status = (status); \
_CHECK( \
HAILO_SUCCESS == __check_success_status, \
__check_success_status, \
_CONSTRUCT_MSG(is_default, "CHECK_SUCCESS failed with status={}", fmt, __check_success_status, ##__VA_ARGS__) \
); \
} while(0)
#define CHECK_SUCCESS(status, ...) _CHECK_SUCCESS(status, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
#define _CHECK_SUCCESS_AS_EXPECTED(status, is_default, fmt, ...) \
do { \
const auto &__check_success_status = (status); \
_CHECK( \
HAILO_SUCCESS == __check_success_status, \
make_unexpected(__check_success_status), \
_CONSTRUCT_MSG(is_default, "CHECK_SUCCESS_AS_EXPECTED failed with status={}", fmt, __check_success_status, ##__VA_ARGS__) \
); \
} while(0)
#define CHECK_SUCCESS_AS_EXPECTED(status, ...) _CHECK_SUCCESS_AS_EXPECTED(status, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
#define _CHECK_EXPECTED(obj, is_default, fmt, ...) \
do { \
const auto &__check_expected_obj = (obj); \
_CHECK( \
__check_expected_obj.has_value(), \
make_unexpected(__check_expected_obj.status()), \
_CONSTRUCT_MSG(is_default, "CHECK_EXPECTED failed with status={}", fmt, __check_expected_obj.status(), ##__VA_ARGS__) \
); \
} while(0)
#define CHECK_EXPECTED(obj, ...) _CHECK_EXPECTED(obj, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
#define _CHECK_EXPECTED_AS_STATUS(obj, is_default, fmt, ...) \
do { \
const auto &__check_expected_obj = (obj); \
_CHECK( \
__check_expected_obj.has_value(), \
__check_expected_obj.status(), \
_CONSTRUCT_MSG(is_default, "CHECK_EXPECTED_AS_STATUS failed with status={}", fmt, __check_expected_obj.status(), ##__VA_ARGS__) \
); \
} while(0)
#define CHECK_EXPECTED_AS_STATUS(obj, ...) _CHECK_EXPECTED_AS_STATUS(obj, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
constexpr bool is_powerof2(size_t v) {
// bit trick
return (v & (v - 1)) == 0;
}
constexpr uint32_t get_nearest_powerof_2(uint32_t value, uint32_t min_power_of_2)
{
assert(value <= 0x80000000);
uint32_t power_of_2 = min_power_of_2;
while (value > power_of_2) {
power_of_2 <<= 1;
}
return power_of_2;
}
} /* namespace hailort */
#endif /* HAILO_UTILS_H_ */

View File

@@ -0,0 +1,349 @@
// SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT
/**
* Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
**/
#ifndef _HAILO_IOCTL_COMMON_H_
#define _HAILO_IOCTL_COMMON_H_
// This value is not easily changeable.
// For example: the channel interrupts ioctls assume we have up to 32 channels
#define MAX_VDMA_CHANNELS (32)
#define SIZE_OF_VDMA_DESCRIPTOR (16)
#define VDMA_DEST_CHANNELS_START (16)
#define CHANNEL_IRQ_TIMESTAMPS_SIZE (128 * 2) // Should be same as MAX_IRQ_TIMESTAMPS_SIZE (hailort_driver.hpp)
#define CHANNEL_IRQ_TIMESTAMPS_SIZE_MASK (CHANNEL_IRQ_TIMESTAMPS_SIZE - 1)
#define INVALID_CHANNEL_HANDLE_VALUE ((uint64_t)-1)
#define INVALID_DRIVER_HANDLE_VALUE ((uintptr_t)-1)
// Used by windows and unix driver to raise the right CPU control handle to the FW. The same as in pcie_service FW
#define FW_ACCESS_CORE_CPU_CONTROL_SHIFT (1)
#define FW_ACCESS_CORE_CPU_CONTROL_MASK (1 << FW_ACCESS_CORE_CPU_CONTROL_SHIFT)
#define FW_ACCESS_CONTROL_INTERRUPT_SHIFT (0)
#define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT)
#define INVALID_VDMA_CHANNEL (0xff)
#ifdef _MSC_VER
#if !defined(bool) && !defined(__cplusplus)
typedef uint8_t bool;
#endif
#if !defined(INT_MAX)
#define INT_MAX 0x7FFFFFFF
#endif
#else
#ifndef __KERNEL__
// include the userspace headers only if this file is included by user space program
// It is discourged to include them when compiling the driver (https://lwn.net/Articles/113349/)
#include <stdint.h>
#include <sys/types.h>
#else
#include <linux/types.h>
#include <linux/limits.h>
#include <linux/kernel.h>
#endif
#if defined(__unix__)
#include <linux/ioctl.h>
#endif
#define _IOW_ _IOW
#define _IOR_ _IOR
#define _IOWR_ _IOWR
#define _IO_ _IO
#define HAILO_GENERAL_IOCTL_MAGIC 'g'
#define HAILO_VDMA_IOCTL_MAGIC 'v'
#define HAILO_WINDOWS_IOCTL_MAGIC 'w'
#endif
#pragma pack(push, 1)
struct hailo_channel_interrupt_timestamp {
uint64_t timestamp_ns;
uint16_t desc_num_processed;
};
// This struct is the same as `enum dma_data_direction` (defined in linux/dma-direction)
enum hailo_dma_data_direction {
HAILO_DMA_BIDIRECTIONAL = 0,
HAILO_DMA_TO_DEVICE = 1,
HAILO_DMA_FROM_DEVICE = 2,
HAILO_DMA_NONE = 3,
/** Max enum value to maintain ABI Integrity */
HAILO_DMA_MAX_ENUM = INT_MAX,
};
// Enum that determines if buffer should be allocated from user space or from driver
enum hailo_allocation_mode {
HAILO_ALLOCATION_MODE_USERSPACE = 0,
HAILO_ALLOCATION_MODE_DRIVER = 1,
/** Max enum value to maintain ABI Integrity */
HAILO_ALLOCATION_MODE_MAX_ENUM = INT_MAX,
};
/* structure used in ioctl HAILO_VDMA_BUFFER_MAP */
struct hailo_vdma_buffer_map_params {
void* user_address; // in
size_t size; // in
enum hailo_dma_data_direction data_direction; // in
uintptr_t allocated_buffer_handle; // in
size_t mapped_handle; // out
};
/* structure used in ioctl HAILO_DESC_LIST_CREATE */
struct hailo_desc_list_create_params {
size_t desc_count; // in
uintptr_t desc_handle; // out
// Note: The dma address is required for CONTEXT_SWITCH firmware controls
uint64_t dma_address; // out
};
/* structure used in ioctl HAILO_WINDOWS_DESC_LIST_MMAP */
struct hailo_windows_desc_list_mmap_params {
uintptr_t desc_handle; // in
size_t size; // in
void* user_address; // out
};
/* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */
struct hailo_desc_list_bind_vdma_buffer_params {
size_t buffer_handle; // in
uintptr_t desc_handle; // in
uint16_t desc_page_size; // in
uint8_t channel_index; // in
};
/* structure used in ioctl HAILO_VDMA_CHANNEL_ENABLE */
struct hailo_vdma_channel_enable_params {
uint32_t channel_index; // in
enum hailo_dma_data_direction direction; // in
// If desc_list_handle is set to valid handle (different than INVALID_DRIVER_HANDLE_VALUE),
// the driver will start the channel with the given descriptors list.
uintptr_t desc_list_handle; // in
bool enable_timestamps_measure; // in
uint64_t channel_handle; // out
};
/* structure used in ioctl HAILO_VDMA_CHANNEL_DISABLE */
struct hailo_vdma_channel_disable_params {
uint32_t channel_index; // in
uint64_t channel_handle; // in
};
/* structure used in ioctl HAILO_VDMA_CHANNEL_WAIT_INT */
struct hailo_vdma_channel_wait_params {
uint32_t channel_index; // in
uint64_t channel_handle; // in
uint64_t timeout_ms; // in
struct hailo_channel_interrupt_timestamp *timestamps; // out
uint32_t timestamps_count; // inout
};
/* structure used in ioctl HAILO_VDMA_CHANNEL_ABORT */
struct hailo_vdma_channel_abort_params {
uint32_t channel_index; // in
uint64_t channel_handle; // in
};
/* structure used in ioctl HAILO_VDMA_CHANNEL_CLEAR_ABORT */
struct hailo_vdma_channel_clear_abort_params {
uint32_t channel_index; // in
uint64_t channel_handle; // in
};
/* structure used in ioctl HAILO_FW_CONTROL */
#define MAX_CONTROL_LENGTH (1500)
#define PCIE_EXPECTED_MD5_LENGTH (16)
/* structure used in ioctl HAILO_FW_CONTROL and HAILO_READ_LOG */
enum hailo_cpu_id {
HAILO_CPU_ID_CPU0 = 0,
HAILO_CPU_ID_CPU1,
HAILO_CPU_ID_NONE,
/** Max enum value to maintain ABI Integrity */
HAILO_CPU_MAX_ENUM = INT_MAX,
};
struct hailo_fw_control {
// expected_md5+buffer_len+buffer must be in this order at the start of the struct
uint8_t expected_md5[PCIE_EXPECTED_MD5_LENGTH];
uint32_t buffer_len;
uint8_t buffer[MAX_CONTROL_LENGTH];
uint32_t timeout_ms;
enum hailo_cpu_id cpu_id;
};
/* structure used in ioctl HAILO_BAR_TRANSFER */
enum hailo_transfer_direction {
TRANSFER_READ = 0,
TRANSFER_WRITE,
/** Max enum value to maintain ABI Integrity */
TRANSFER_MAX_ENUM = INT_MAX,
};
struct hailo_bar_transfer_params {
enum hailo_transfer_direction transfer_direction; // in
uint32_t bar_index; // in
off_t offset; // in
size_t count; // in
void* buffer; // in/out
};
/* structure used in ioctl HAILO_VDMA_CHANNEL_REGISTERS */
struct hailo_channel_registers_params {
enum hailo_transfer_direction transfer_direction; // in
off_t offset; // in
size_t size; // in
uint32_t data; // in/out
};
/* structure used in ioctl HAILO_VDMA_BUFFER_SYNC */
enum hailo_vdma_buffer_sync_type {
HAILO_SYNC_FOR_HOST,
HAILO_SYNC_FOR_DEVICE,
/** Max enum value to maintain ABI Integrity */
HAILO_SYNC_MAX_ENUM = INT_MAX,
};
struct hailo_vdma_buffer_sync_params {
size_t handle; // in
enum hailo_vdma_buffer_sync_type sync_type; // in
void* buffer_address; // in
uint64_t buffer_size; // in
};
/* structure used in ioctl HAILO_READ_NOTIFICATION */
struct hailo_d2h_notification {
size_t buffer_len; // out
uint8_t buffer[MAX_CONTROL_LENGTH]; // out
};
enum hailo_board_type {
HAILO8 = 0,
HAILO_MERCURY,
HAILO_BOARD_COUNT,
HAILO_INVALID_BOARD = 0xFFFFFFFF,
};
enum hailo_dma_type {
HAILO_DMA_TYPE_PCIE,
HAILO_DMA_TYPE_DRAM,
/** Max enum value to maintain ABI Integrity */
HAILO_DMA_TYPE_MAX_ENUM = INT_MAX,
};
struct hailo_device_properties {
uint16_t desc_max_page_size;
enum hailo_board_type board_type;
enum hailo_allocation_mode allocation_mode;
enum hailo_dma_type dma_type;
};
struct hailo_driver_info {
uint32_t major_version;
uint32_t minor_version;
uint32_t revision_version;
};
struct hailo_read_log_params {
enum hailo_cpu_id cpu_id; // in
uint8_t *buffer; // out
size_t buffer_size; // in
size_t read_bytes; // out
};
struct hailo_allocate_buffer_params {
size_t buffer_size; // in
uintptr_t buffer_handle; // out
};
struct hailo_mark_as_in_use_params {
bool in_use; // out
};
#pragma pack(pop)
enum hailo_general_ioctl_code {
HAILO_BAR_TRANSFER_CODE,
HAILO_FW_CONTROL_CODE,
HAILO_READ_NOTIFICATION_CODE,
HAILO_DISABLE_NOTIFICATION_CODE,
HAILO_QUERY_DEVICE_PROPERTIES_CODE,
HAILO_QUERY_DRIVER_INFO_CODE,
HAILO_READ_LOG_CODE,
HAILO_RESET_NN_CORE_CODE,
// Must be last
HAILO_GENERAL_IOCTL_MAX_NR,
};
#define HAILO_BAR_TRANSFER _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_BAR_TRANSFER_CODE, struct hailo_bar_transfer_params)
#define HAILO_FW_CONTROL _IOWR_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_FW_CONTROL_CODE, struct hailo_fw_control)
#define HAILO_READ_NOTIFICATION _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_READ_NOTIFICATION_CODE, struct hailo_d2h_notification)
#define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE)
#define HAILO_QUERY_DEVICE_PROPERTIES _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_QUERY_DEVICE_PROPERTIES_CODE, struct hailo_device_properties)
#define HAILO_QUERY_DRIVER_INFO _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_QUERY_DRIVER_INFO_CODE, struct hailo_driver_info)
#define HAILO_READ_LOG _IOWR_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params)
#define HAILO_RESET_NN_CORE _IO_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE)
enum hailo_vdma_ioctl_code {
HAILO_VDMA_CHANNEL_ENABLE_CODE,
HAILO_VDMA_CHANNEL_DISABLE_CODE,
HAILO_VDMA_CHANNEL_WAIT_INT_CODE,
HAILO_VDMA_CHANNEL_ABORT_CODE,
HAILO_VDMA_CHANNEL_CLEAR_ABORT_CODE,
HAILO_VDMA_CHANNEL_REGISTERS_CODE,
HAILO_VDMA_BUFFER_MAP_CODE,
HAILO_VDMA_BUFFER_UNMAP_CODE,
HAILO_VDMA_BUFFER_SYNC_CODE,
HAILO_DESC_LIST_CREATE_CODE,
HAILO_DESC_LIST_RELEASE_CODE,
HAILO_DESC_LIST_BIND_VDMA_BUFFER_CODE,
HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE,
HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE,
HAILO_MARK_AS_IN_USE_CODE,
// Must be last
HAILO_VDMA_IOCTL_MAX_NR,
};
#define HAILO_VDMA_CHANNEL_ENABLE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_ENABLE_CODE, struct hailo_vdma_channel_enable_params)
#define HAILO_VDMA_CHANNEL_DISABLE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_DISABLE_CODE, struct hailo_vdma_channel_disable_params)
#define HAILO_VDMA_CHANNEL_WAIT_INT _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_WAIT_INT_CODE, struct hailo_vdma_channel_wait_params)
#define HAILO_VDMA_CHANNEL_ABORT _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_ABORT_CODE, struct hailo_vdma_channel_abort_params)
#define HAILO_VDMA_CHANNEL_CLEAR_ABORT _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_CLEAR_ABORT_CODE, struct hailo_vdma_channel_clear_abort_params)
#define HAILO_VDMA_CHANNEL_REGISTERS _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_REGISTERS_CODE, struct hailo_channel_registers_params)
#define HAILO_VDMA_BUFFER_MAP _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_MAP_CODE, struct hailo_vdma_buffer_map_params)
#define HAILO_VDMA_BUFFER_UNMAP _IO_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_UNMAP_CODE)
#define HAILO_VDMA_BUFFER_SYNC _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_SYNC_CODE, struct hailo_vdma_buffer_sync_params)
#define HAILO_DESC_LIST_CREATE _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_CREATE_CODE, struct hailo_desc_list_create_params)
#define HAILO_DESC_LIST_RELEASE _IO_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_RELEASE_CODE)
#define HAILO_DESC_LIST_BIND_VDMA_BUFFER _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_BIND_VDMA_BUFFER_CODE, struct hailo_desc_list_bind_vdma_buffer_params)
#define HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, struct hailo_allocate_buffer_params)
#define HAILO_VDMA_LOW_MEMORY_BUFFER_FREE _IO_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE)
#define HAILO_MARK_AS_IN_USE _IOW_(HAILO_VDMA_IOCTL_MAGIC, HAILO_MARK_AS_IN_USE_CODE, struct hailo_mark_as_in_use_params)
enum hailo_windows_ioctl_code {
HAILO_WINDOWS_DESC_LIST_MMAP_CODE,
// Must be last
HAILO_WINDOWS_IOCTL_MAX_NR,
};
#define HAILO_WINDOWS_DESC_LIST_MMAP _IOWR_(HAILO_WINDOWS_IOCTL_MAGIC, HAILO_WINDOWS_DESC_LIST_MMAP_CODE, struct hailo_windows_desc_list_mmap_params)
#endif /* _HAILO_IOCTL_COMMON_H_ */

View File

@@ -0,0 +1,69 @@
cmake_minimum_required(VERSION 3.0.0)
set(HAILORTCLI_CPP_FILES
hailortcli.cpp
command.cpp
scan_command.cpp
run_command.cpp
inference_progress.cpp
power_measurement_command.cpp
fw_control_command.cpp
fw_update_command.cpp
ssb_update_command.cpp
infer_stats_printer.cpp
sensor_config_command.cpp
board_config_command.cpp
fw_config_command.cpp
fw_logger_command.cpp
fw_config_serializer.cpp
common.cpp
benchmark_command.cpp
temp_measurement.cpp
parse_hef_command.cpp
graph_printer.cpp
)
if(UNIX)
# Unix only modules
set(HAILORTCLI_CPP_FILES ${HAILORTCLI_CPP_FILES}
udp_rate_limiter_command.cpp
# TODO: We dont compile download_action_list_command on windows, as it uses packed enums (HRT-5919)
download_action_list_command.cpp
)
endif()
# 'config_definitions_json_file' is used in generate_definitions_json_str.in for configure_file()
file(READ "${PROJECT_SOURCE_DIR}/common/config_definitions.json" config_definitions_json_file)
set(config_definitions_header ${CMAKE_CURRENT_BINARY_DIR}/definitions_json.auto.hpp)
set(CONFIG_DEFENITIONS_IN ${PROJECT_SOURCE_DIR}/hailort/hailortcli/generate_definitions_json_str.in)
configure_file(${CONFIG_DEFENITIONS_IN} ${config_definitions_header})
add_executable(hailortcli
${config_definitions_header}
${HAILORTCLI_CPP_FILES}
${HAILORT_COMMON_CPP_SOURCES}
${PROJECT_SOURCE_DIR}/common/src/firmware_header_utils.c
${PROJECT_SOURCE_DIR}/common/src/md5.c
${HAILORT_SRC_DIR}/pipeline.cpp
${HAILO_FULL_OS_DIR}/event.cpp
)
target_compile_options(hailortcli PRIVATE ${HAILORT_COMPILE_OPTIONS})
set_property(TARGET hailortcli PROPERTY CXX_STANDARD 14)
set_property(TARGET hailortcli PROPERTY INSTALL_RPATH "$ORIGIN") # Need to add "${CMAKE_INSTALL_LIBDIR}" when installing with cmake to /usr/local
target_link_libraries(hailortcli libhailort CLI11::CLI11 nlohmann_json spdlog::spdlog readerwriterqueue)
target_link_libraries(hailortcli DotWriter)
if(WIN32)
target_link_libraries(hailortcli Ws2_32 Iphlpapi Shlwapi)
endif()
target_include_directories(hailortcli
PRIVATE
${CMAKE_CURRENT_BINARY_DIR} # CMAKE_CURRENT_BINARY_DIR is necessary for config_definitions_header
${HAILORT_COMMON_DIR}
${COMMON_INC_DIR}
${HAILORT_SRC_DIR}
)
install(TARGETS hailortcli
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
CONFIGURATIONS Release)

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file benchmark_command.cpp
* @brief measure basic performance on compiled network
**/
#include "benchmark_command.hpp"
#include "hailortcli.hpp"
#include "infer_stats_printer.hpp"
#include <iostream>
BenchmarkCommand::BenchmarkCommand(CLI::App &parent_app) :
Command(parent_app.add_subcommand("benchmark", "Measure basic performance on compiled network")),
m_params({})
{
add_device_options(m_app, m_params.device_params);
add_vdevice_options(m_app, m_params.device_params);
m_params.measure_overall_latency = false;
m_params.power_measurement.measure_current = false;
m_params.show_progress = true;
m_params.transform.format_type = HAILO_FORMAT_TYPE_AUTO;
m_app->add_option("hef", m_params.hef_path, "Path of the HEF to load")
->check(CLI::ExistingFile)
->required();
m_app->add_option("-t, --time-to-run", m_time, "Measurement time in seconds per hw_only/streaming/latency measurement mode")
->check(CLI::PositiveNumber)
->default_val(15);
m_app->add_option("--no-power", m_not_measure_power, "Skip power measurement, even if the platform supports it. The default value is False")
->default_val("false");
m_app->add_option("--batch-size", m_params.batch_size, "Inference batch size (default is 1)")
->default_val(1);
m_app->add_option("--input-files", m_params.inputs_name_and_file_path, " The input files need to be in UINT8 before transformations.")
->check(InputNameToFileMap);
m_app->add_option("--csv", m_csv_file_path, "If set print the output as csv to the specified path");
auto measure_power_group = m_app->add_option_group("Measure Power");
CLI::Option *power_sampling_period = measure_power_group->add_option("--sampling-period",
m_params.power_measurement.sampling_period, "Sampling Period");
CLI::Option *power_averaging_factor = measure_power_group->add_option("--averaging-factor",
m_params.power_measurement.averaging_factor, "Averaging Factor");
PowerMeasurementSubcommand::init_sampling_period_option(power_sampling_period);
PowerMeasurementSubcommand::init_averaging_factor_option(power_averaging_factor);
// TODO HRT-5363 support multiple devices
m_app->parse_complete_callback([this]() {
PARSE_CHECK((this->m_params.device_params.vdevice_params.device_count == 1) || this->m_csv_file_path.empty() || this->m_not_measure_power,
"Writing power measurements in csv format is not supported for multiple devices");
});
}
hailo_status BenchmarkCommand::execute()
{
std::cout << "Starting Measurements..." << std::endl;
std::cout << "Measuring FPS in hw_only mode" << std::endl;
auto hw_only_mode_info = hw_only_mode();
CHECK_EXPECTED_AS_STATUS(hw_only_mode_info, "hw_only measuring failed");
std::cout << "Measuring FPS " << (!m_not_measure_power ? "and Power " : "") << "in streaming mode" << std::endl;
auto streaming_mode_info = fps_streaming_mode();
CHECK_EXPECTED_AS_STATUS(streaming_mode_info, "FPS in streaming mode failed");
std::cout << "Measuring HW Latency" << std::endl;
auto latency_info = latency();
CHECK_EXPECTED_AS_STATUS(latency_info, "Latency measuring failed");
std::cout << std::endl;
std::cout << "=======" << std::endl;
std::cout << "Summary" << std::endl;
std::cout << "=======" << std::endl;
std::cout << "FPS (hw_only) = " << hw_only_mode_info->fps().value() <<std::endl;
std::cout << " (streaming) = " << streaming_mode_info->fps().value() <<std::endl;
if (auto hw_latency = latency_info->hw_latency()) {
std::cout << "Latency (hw) = " << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value()) << " ms" << std::endl;
}
if (auto overall_latency = latency_info->overall_latency()) {
std::cout << " (overall) = " << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value()) << " ms" << std::endl;
}
if (!m_not_measure_power) {
for (const auto &pair : streaming_mode_info->m_power_measurements) {
std::cout << "Device " << pair.first << ":" << std::endl;
const auto &data = pair.second->data();
const auto &power_units = pair.second->power_units();
std::cout << " Power in streaming mode (average) = " << data.average_value << " " << power_units << std::endl;
std::cout << " (max) = " << data.max_value << " " << power_units << std::endl;
}
}
if (!m_csv_file_path.empty()){
m_params.csv_output = m_csv_file_path;
auto printer = InferStatsPrinter::create(m_params, false);
CHECK_EXPECTED_AS_STATUS(printer, "Failed to initialize infer stats printer");
printer->print_benchmark_csv_header();
printer->print_benchmark_csv(m_params.hef_path, hw_only_mode_info.release(),
streaming_mode_info.release(), latency_info.release());
}
return HAILO_SUCCESS;
}
Expected<NetworkGroupInferResult> BenchmarkCommand::hw_only_mode()
{
m_params.transform.transform = (m_params.inputs_name_and_file_path.size() > 0);
m_params.power_measurement.measure_power = false;
m_params.measure_latency = false;
m_params.mode = InferMode::HW_ONLY;
m_params.time_to_run = m_time;
return run_command_hef(m_params);
}
Expected<NetworkGroupInferResult> BenchmarkCommand::fps_streaming_mode()
{
m_params.power_measurement.measure_power = !m_not_measure_power;
m_params.mode = InferMode::STREAMING;
m_params.measure_latency = false;
m_params.transform.transform = true;
m_params.transform.quantized = false;
m_params.time_to_run = m_time;
return run_command_hef(m_params);
}
Expected<NetworkGroupInferResult> BenchmarkCommand::latency()
{
m_params.power_measurement.measure_power = false;
m_params.measure_latency = true;
m_params.mode = InferMode::STREAMING;
m_params.transform.transform = true;
m_params.transform.quantized = false;
m_params.time_to_run = m_time;
return run_command_hef(m_params);
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file benchmarks_command.hpp
* @brief measure basic performance on compiled network
**/
#ifndef _HAILO_BENCHMARK_COMMAND_HPP_
#define _HAILO_BENCHMARK_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "run_command.hpp"
#include "CLI/CLI.hpp"
class BenchmarkCommand : public Command {
public:
explicit BenchmarkCommand(CLI::App &parent_app);
hailo_status execute() override;
private:
Expected<NetworkGroupInferResult> hw_only_mode();
Expected<NetworkGroupInferResult> fps_streaming_mode();
Expected<NetworkGroupInferResult> latency();
inference_runner_params m_params;
bool m_not_measure_power;
uint32_t m_time;
std::string m_csv_file_path;
};
#endif /*_HAILO_BENCHMARK_COMMAND_HPP_*/

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file board_config_command.cpp
* @brief Board configuration command (fw static configuration).
**/
#include "board_config_command.hpp"
#include "common/file_utils.hpp"
#include <fstream>
BoardConfigCommand::BoardConfigCommand(CLI::App &parent_app) :
ContainerCommand(parent_app.add_subcommand("board-config", "Board configuration tool"))
{
// This will make the board-config command to be hidden in the --help print in the command line.
m_app->group("");
add_subcommand<BoardConfigReadSubcommand>();
add_subcommand<BoardConfigWriteSubcommand>();
}
BoardConfigReadSubcommand::BoardConfigReadSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("read", "Read board configuration from device"))
{
m_app->add_option("output_file", m_output_file_path, "File path to dump board configuration into.")
->required();
}
hailo_status BoardConfigReadSubcommand::execute_on_device(Device &device)
{
auto buffer = device.read_board_config();
CHECK_EXPECTED_AS_STATUS(buffer, "Failed reading board config from device");
auto output_file = std::ofstream(m_output_file_path, std::ios::out | std::ios::binary);
CHECK(output_file.is_open(), HAILO_OPEN_FILE_FAILURE, "Failed opening output file {} with errno: {}", m_output_file_path, errno);
output_file.write(reinterpret_cast<char*>(buffer->data()), buffer->size());
CHECK(output_file.good(), HAILO_FILE_OPERATION_FAILURE, "Failed writing board config into file {}.", m_output_file_path);
return HAILO_SUCCESS;
}
BoardConfigWriteSubcommand::BoardConfigWriteSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("write", "Write board configuration to device"))
{
m_app->add_option("input_file", m_input_file_path, "Board config binary file path.")
->check(CLI::ExistingFile)
->required();
}
hailo_status BoardConfigWriteSubcommand::execute_on_device(Device &device)
{
auto buffer = read_binary_file(m_input_file_path);
CHECK_EXPECTED_AS_STATUS(buffer);
hailo_status status = device.write_board_config(MemoryView(buffer.value()));
CHECK_SUCCESS(status, "Failed writing board config to device.");
return HAILO_SUCCESS;
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file board_config_command.hpp
* @brief Firmware configuration command.
**/
#ifndef _HAILO_BOARD_CONFIG_COMMAND_HPP_
#define _HAILO_BOARD_CONFIG_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "CLI/CLI.hpp"
class BoardConfigReadSubcommand final : public DeviceCommand {
public:
explicit BoardConfigReadSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_output_file_path;
};
class BoardConfigWriteSubcommand final : public DeviceCommand {
public:
explicit BoardConfigWriteSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_input_file_path;
};
class BoardConfigCommand final : public ContainerCommand {
public:
explicit BoardConfigCommand(CLI::App &parent_app);
};
#endif /* _HAILO_BOARD_CONFIG_COMMAND_HPP_ */

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file command.cpp
* @brief Base classes for hailortcli commands.
**/
#include "command.hpp"
Command::Command(CLI::App *app) :
m_app(app)
{
}
ContainerCommand::ContainerCommand(CLI::App *app) :
Command(app)
{
m_app->require_subcommand(1);
}
hailo_status ContainerCommand::execute()
{
for (auto &command : m_subcommands) {
if (command->parsed()) {
return command->execute();
}
}
LOGGER__ERROR("No subommand found..");
return HAILO_NOT_FOUND;
}
DeviceCommand::DeviceCommand(CLI::App *app) :
Command(app)
{
add_device_options(m_app, m_device_params);
}
hailo_status DeviceCommand::execute()
{
auto device = create_device(m_device_params);
if (!device) {
return device.status();
}
return execute_on_device(*device.value());
}
PcieDeviceCommand::PcieDeviceCommand(CLI::App *app) :
Command(app)
{
auto group = app->add_option_group("PCIE Device Options");
// PCIe options
group->add_option("-s,--bdf", m_pcie_device_params.pcie_bdf,
"Device id ([<domain>]:<bus>:<device>.<func>, same as in lspci command)")
->default_val("");
}
hailo_status PcieDeviceCommand::execute()
{
auto device = create_pcie_device(m_pcie_device_params);
if (!device) {
return device.status();
}
return execute_on_device(*device.value());
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file command.hpp
* @brief Base classes for hailortcli commands.
**/
#ifndef _HAILO_COMMAND_HPP_
#define _HAILO_COMMAND_HPP_
#include "hailortcli.hpp"
#include "CLI/CLI.hpp"
class Command {
public:
explicit Command(CLI::App *app);
virtual ~Command() = default;
virtual hailo_status execute() = 0;
bool parsed() const
{
return m_app->parsed();
}
void set_description(const std::string &new_desc)
{
m_app->description(new_desc);
}
void set_footer(const std::string &new_footer)
{
m_app->footer(new_footer);
}
protected:
CLI::App *m_app;
};
// Command that only contains list of subcommand
class ContainerCommand : public Command {
public:
explicit ContainerCommand(CLI::App *app);
virtual hailo_status execute() override final;
protected:
template<typename CommandType>
CommandType &add_subcommand(bool hidden = false)
{
// Unnamed "option groups" hide subcommands/options from the help message
// (see https://github.com/CLIUtils/CLI11/blob/main/README.md)
auto *parent = hidden ? m_app->add_option_group("") : m_app;
auto command = std::make_shared<CommandType>(*parent);
m_subcommands.push_back(command);
return *command;
}
private:
std::vector<std::shared_ptr<Command>> m_subcommands;
};
class DeviceCommand : public Command {
public:
explicit DeviceCommand(CLI::App *app);
virtual hailo_status execute() override final;
protected:
virtual hailo_status execute_on_device(Device &device) = 0;
private:
hailo_device_params m_device_params;
};
class PcieDeviceCommand : public Command {
public:
explicit PcieDeviceCommand(CLI::App *app);
virtual hailo_status execute() override final;
protected:
virtual hailo_status execute_on_device(Device &device) = 0;
private:
hailo_pcie_params m_pcie_device_params;
};
#endif /* _HAILO_COMMAND_HPP_ */

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file common.cpp
* @brief Common functions.
**/
#include "common.hpp"
#include "common/utils.hpp"
#include <iostream>
#include <sstream>
#include <chrono>
#include <ctime>
std::string CliCommon::duration_to_string(std::chrono::seconds secs)
{
using namespace std::chrono;
using namespace std::chrono_literals;
bool neg = (secs < 0s);
if (neg) {
secs = -secs;
}
auto h = duration_cast<hours>(secs);
secs -= h;
auto m = duration_cast<minutes>(secs);
secs -= m;
std::stringstream result;
if (neg) {
result << '-';
}
if (h < 10h) {
result << '0';
}
result << (h/1h) << ':';
if (m < 10min) {
result << '0';
}
result << m/1min << ':';
if (secs < 10s) {
result << '0';
}
result << secs/1s;
return result.str();
}
Expected<std::string> CliCommon::current_time_to_string()
{
const auto curr_time = std::time(nullptr);
CHECK_AS_EXPECTED(static_cast<std::time_t>(-1) != curr_time, HAILO_INTERNAL_FAILURE, "std::time failed");
const auto *local_time = std::localtime(&curr_time);
CHECK_AS_EXPECTED(nullptr != local_time, HAILO_INTERNAL_FAILURE, "std::localtime failed");
std::stringstream result;
// Standard date and time string (see: https://en.cppreference.com/w/cpp/io/manip/put_time)
result << std::put_time(local_time, "%c");
return result.str();
}
void CliCommon::reset_cursor(size_t lines_count)
{
for (size_t i = 0; i < lines_count; i++) {
std::cout << FORMAT_CURSOR_UP_LINE;
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file common.hpp
* @brief Common functions.
**/
#ifndef _HAILO_HAILORTCLI_COMMON_HPP_
#define _HAILO_HAILORTCLI_COMMON_HPP_
#include "CLI/CLI.hpp"
#include "common/filesystem.hpp"
#include <chrono>
#include <sstream>
using namespace hailort;
// http://www.climagic.org/mirrors/VT100_Escape_Codes.html
#define FORMAT_CLEAR_LINE "\033[2K\r"
#define FORMAT_CURSOR_UP_LINE "\033[F"
class CliCommon final
{
public:
CliCommon() = delete;
static std::string duration_to_string(std::chrono::seconds secs);
static Expected<std::string> current_time_to_string();
static void reset_cursor(size_t number_of_lines);
};
// Validators
struct FileSuffixValidator : public CLI::Validator {
FileSuffixValidator(const std::string &suffix) {
name_ = "FILE_SUFFIX";
func_ = [suffix](const std::string &filename) {
if (!Filesystem::has_suffix(filename, suffix)) {
std::stringstream error_message;
error_message << "File '" << filename << "' does not end with suffix '"
<< suffix << "'." << std::endl;
return error_message.str();
}
// Success
return std::string();
};
}
};
#endif /* _HAILO_HAILORTCLI_COMMON_HPP_ */

View File

@@ -0,0 +1,429 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file download_action_list_command.cpp
* @brief Download action list command implementation
**/
#include "download_action_list_command.hpp"
#include "common.hpp"
#include "common/file_utils.hpp"
#include "md5.h"
#include <iostream>
constexpr int DownloadActionListCommand::INVALID_NUMERIC_VALUE;
DownloadActionListCommand::DownloadActionListCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("action-list", "Download action list data, for run time profiler"))
{
static const char *JSON_SUFFIX = ".json";
m_app->add_option("--output-file", m_output_file_path, "Output file path")
->default_val("context_action_list.json")
->check(FileSuffixValidator(JSON_SUFFIX));
}
hailo_status DownloadActionListCommand::execute(Device &device, const std::string &output_file_path,
const ConfiguredNetworkGroupVector &network_groups, const std::string &hef_file_path)
{
std::cout << "> Writing action list to '" << output_file_path << "'... ";
auto curr_time = CliCommon::current_time_to_string();
CHECK_EXPECTED_AS_STATUS(curr_time);
// TODO: Get real clock rate (HRT-5998)
static const uint32_t CLOCK_CYCLE = 200;
ordered_json action_list_json = {
{"version", ACTION_LIST_FORMAT_VERSION()},
{"creation_time", curr_time.release()},
{"clock_cycle_MHz", CLOCK_CYCLE},
{"hef", json({})}
};
if (!hef_file_path.empty()) {
auto hef_info = parse_hef_metadata(hef_file_path);
CHECK_EXPECTED_AS_STATUS(hef_info);
action_list_json["hef"] = hef_info.release();
}
auto network_groups_list_json = parse_network_groups(device, network_groups);
CHECK_EXPECTED_AS_STATUS(network_groups_list_json);
action_list_json["network_groups"] = network_groups_list_json.release();
CHECK_SUCCESS(write_json(action_list_json, output_file_path));
std::cout << "done." << std::endl;
return HAILO_SUCCESS;
}
hailo_status DownloadActionListCommand::execute_on_device(Device &device)
{
return execute(device, m_output_file_path);
}
Expected<ordered_json> DownloadActionListCommand::parse_hef_metadata(const std::string &hef_file_path)
{
CHECK_AS_EXPECTED(is_valid_hef(hef_file_path), HAILO_INTERNAL_FAILURE,
"Hef '{}' is not valid", hef_file_path);
auto hef_md5 = calc_md5_hexdigest(hef_file_path);
CHECK_EXPECTED(hef_md5);
ordered_json hef_info_json = {
{"path", hef_file_path},
{"file_hash", hef_md5.release()}
};
return hef_info_json;
}
bool DownloadActionListCommand::is_valid_hef(const std::string &hef_file_path)
{
// Open hef, to check that it's valid
const auto hef = Hef::create(hef_file_path);
return hef.has_value();
}
Expected<std::string> DownloadActionListCommand::calc_md5_hexdigest(const std::string &hef_file_path)
{
auto hef_bin = read_binary_file(hef_file_path);
CHECK_EXPECTED(hef_bin);
MD5_CTX md5_ctx{};
MD5_SUM_t md5_sum{};
MD5_Init(&md5_ctx);
MD5_Update(&md5_ctx, hef_bin->data(), hef_bin->size());
MD5_Final(md5_sum, &md5_ctx);
std::stringstream hexdigest;
for (uint32_t i = 0; i < ARRAY_ENTRIES(md5_sum); i++) {
// cast to int needed for proper formatting
hexdigest << std::hex << static_cast<int>(md5_sum[i]);
}
return hexdigest.str();
}
hailo_status DownloadActionListCommand::write_json(const ordered_json &json_obj, const std::string &output_file_path,
int tab_width)
{
std::ofstream output_file(output_file_path);
CHECK(output_file, HAILO_INTERNAL_FAILURE, "Failed opening file '{}'", output_file_path);
output_file << std::setw(tab_width) << json_obj << std::endl;
CHECK(!output_file.bad() && !output_file.fail(), HAILO_INTERNAL_FAILURE,
"Failed writing to file '{}'", output_file_path);
return HAILO_SUCCESS;
}
Expected<ordered_json> DownloadActionListCommand::parse_action_data(uint32_t base_address, uint8_t *action,
uint32_t current_buffer_offset, uint32_t *action_length, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type,
uint32_t timestamp, uint8_t sub_action_index, bool sub_action_index_set, bool *is_repeated, uint8_t *num_repeated,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type)
{
ordered_json action_json {
{"address", base_address + current_buffer_offset},
{"timestamp", timestamp},
{"type", action_type}
};
if (sub_action_index_set) {
action_json["sub_action_index"] = sub_action_index;
}
size_t action_length_local = 0;
json data_json;
switch (action_type) {
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION:
{
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__repeated_action_header_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__repeated_action_header_t);
const auto *repeated_header = reinterpret_cast<CONTEXT_SWITCH_DEFS__repeated_action_header_t *>(action);
*is_repeated = true;
*num_repeated = repeated_header->count;
*sub_action_type = repeated_header->sub_action_type;
break;
}
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_LCU_INTERRUPT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_SEQUENCER_DONE_INTERRUPT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_INPUT_CHANNEL_TRANSFER_DONE_INTERRUPT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_DMA_IDLE_ACTION:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_NMS_IDLE:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_OUTPUT_CHANNEL_TRANSFER_DONE_INTERRUPT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_MODULE_CONFIG_DONE_INTERRUPT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_APPLICATION_CHANGE_INTERRUPT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__read_vdma_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__read_vdma_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_TRIGGER_SEQUENCER:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_DATA_FROM_VDMA_CHANNEL:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__fetch_data_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__fetch_data_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_VDMA_CHANNEL:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_NON_DEFAULT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DISABLE_LCU:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_INPUT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_OUTPUT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_INPUT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_OUTPUT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_INPUT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_OUTPUT:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_CHANGE_VDMA_TO_STREAM_MAPPING:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ADD_DDR_PAIR_INFO:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DDR_BUFFERING_START:
data_json = json({});
action_length_local = 0;
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_cfg_channel_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_cfg_channel_t);
break;
case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_CFG_CHANNEL:
data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t *>(action);
action_length_local = sizeof(CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t);
break;
default:
std::cerr << "PARSING ERROR ! unknown action main type" << std::endl;
return make_unexpected(HAILO_INTERNAL_FAILURE);
}
action_json["data"] = data_json;
*action_length = static_cast<uint32_t>(action_length_local);
return action_json;
}
Expected<ordered_json> DownloadActionListCommand::parse_single_repeated_action(uint32_t base_address,
uint8_t *action, uint32_t current_buffer_offset, uint32_t *action_length,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type, uint32_t timestamp, uint8_t index_in_repeated_block)
{
static const bool SET_SUB_ACTION_INDEX = true;
return parse_action_data(base_address, action, current_buffer_offset, action_length,
action_type, timestamp, index_in_repeated_block, SET_SUB_ACTION_INDEX);
}
Expected<ordered_json> DownloadActionListCommand::parse_single_action(uint32_t base_address,
uint8_t *context_action_list, uint32_t current_buffer_offset, uint32_t *action_length, bool *is_repeated,
uint8_t *num_repeated, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type, uint32_t *time_stamp)
{
const auto action_length_local = sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t);
const auto *action_header = reinterpret_cast<CONTEXT_SWITCH_DEFS__common_action_header_t *>(&context_action_list[current_buffer_offset]);
const auto time_stamp_local = CONTEXT_SWITCH_DEFS__TIMESTAMP_INIT_VALUE - action_header->time_stamp;
current_buffer_offset += static_cast<uint32_t>(sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t));
static const bool DONT_SET_SUB_ACTION_INDEX = false;
uint32_t action_data_length = 0;
auto json = parse_action_data(base_address, &context_action_list[current_buffer_offset], current_buffer_offset, &action_data_length,
action_header->action_type, time_stamp_local, 0, DONT_SET_SUB_ACTION_INDEX, is_repeated, num_repeated, sub_action_type);
CHECK_EXPECTED(json);
*action_length = static_cast<uint32_t>(action_length_local + action_data_length);
*time_stamp = time_stamp_local;
return json.release();
}
Expected<ordered_json> DownloadActionListCommand::parse_context(uint32_t action_list_base_address,
uint8_t *context_action_list, uint16_t action_list_size, uint32_t batch_counter)
{
ordered_json context_json {
{"action_list_base_address", action_list_base_address},
{"action_list_size", action_list_size },
{"batch_counter", batch_counter}
};
ordered_json action_list_json;
uint16_t current_buffer_offset = 0;
while (current_buffer_offset < action_list_size) {
bool is_repeated = false;
uint8_t num_repeated = 0;
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t sub_action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT;
uint32_t single_action_length = 0;
uint32_t timestamp = 0;
auto action_json = parse_single_action(action_list_base_address, context_action_list,
current_buffer_offset, &single_action_length, &is_repeated, &num_repeated, &sub_action_type, &timestamp);
CHECK_EXPECTED(action_json);
current_buffer_offset = (uint16_t)(current_buffer_offset + single_action_length);
action_list_json.emplace_back(action_json.release());
if (is_repeated) {
for (uint8_t index_in_repeated_block = 0; index_in_repeated_block < num_repeated; index_in_repeated_block++) {
uint32_t sub_action_length = 0;
auto repeated_action_json = parse_single_repeated_action(action_list_base_address,
context_action_list + current_buffer_offset, current_buffer_offset, &sub_action_length,
sub_action_type, timestamp, index_in_repeated_block);
CHECK_EXPECTED(repeated_action_json);
current_buffer_offset = (uint16_t)(current_buffer_offset + sub_action_length);
action_list_json.emplace_back(repeated_action_json.release());
}
}
}
CHECK_AS_EXPECTED(current_buffer_offset == action_list_size, HAILO_INTERNAL_FAILURE,
"PARSING ERROR ! Reached forbidden memory space");
context_json["actions"] = action_list_json;
return context_json;
}
double DownloadActionListCommand::get_accumulator_mean_value(const AccumulatorPtr &accumulator, double default_value)
{
auto mean_value = accumulator->mean();
return mean_value ? mean_value.value() : default_value;
}
Expected<ordered_json> DownloadActionListCommand::parse_network_groups(Device &device, const ConfiguredNetworkGroupVector &network_groups)
{
const auto number_of_contexts_per_network_group = device.get_number_of_contexts_per_network_group();
CHECK_EXPECTED(number_of_contexts_per_network_group);
ordered_json network_group_list_json;
uint8_t global_context_index = 0;
for (uint32_t network_group_index = 0; network_group_index < number_of_contexts_per_network_group->size(); network_group_index++) {
// TODO: network_group_name via Hef::get_network_groups_names (HRT-5997)
ordered_json network_group_json = {
{"mean_activation_time_ms", INVALID_NUMERIC_VALUE},
{"mean_deactivation_time_ms", INVALID_NUMERIC_VALUE},
{"contexts", json::array()}
};
// We assume the the order of the network_groups in the ConfiguredNetworkGroupVector and in the action_list
// downloaded from the fw is the same. If the received ConfiguredNetworkGroupVector is empty, we leave the
// mean_de/activation_time_ms with their default values (INVALID_NUMERIC_VALUE).
if (network_groups.size() > network_group_index) {
network_group_json["mean_activation_time_ms"] = get_accumulator_mean_value(
network_groups[network_group_index]->get_activation_time_accumulator());
network_group_json["mean_deactivation_time_ms"] = get_accumulator_mean_value(
network_groups[network_group_index]->get_deactivation_time_accumulator());
}
const auto num_contexts_in_network_group = number_of_contexts_per_network_group.value()[network_group_index];
for (uint8_t i = 0; i < num_contexts_in_network_group; i++) {
uint32_t batch_counter = 0;
uint32_t base_address = 0;
auto action_list = device.download_context_action_list(global_context_index, &base_address, &batch_counter);
CHECK_EXPECTED(action_list);
// Needs to fit in 2 bytes due to firmware limitation of action list size
CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(action_list->size()), HAILO_INTERNAL_FAILURE,
"Action list size is expected to fit in 2B. actual size is {}", action_list->size());
auto context_json = parse_context(base_address, action_list->data(),
static_cast<uint16_t>(action_list->size()), batch_counter);
CHECK_EXPECTED(context_json);
network_group_json["contexts"].emplace_back(context_json.release());
// Device::get_number_of_contexts_per_network_group guarantees no overflow
global_context_index++;
}
network_group_list_json.emplace_back(network_group_json);
}
return network_group_list_json;
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__read_vdma_action_data_t& data) {
j = json{{"descriptors_count", data.descriptors_count}, {"channel_index", data.cfg_channel_number}};
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t& data) {
j = json{{"channel_index", data.cfg_channel_number}};
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t& data) {
const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t& data) {
const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t& data) {
const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t& data) {
j = json{{"vdma_channel_index", data.vdma_channel_index}, {"stream_index", data.stream_index},
{"type", data.is_dummy_stream ? "dummy" : "active"}};
}
void to_json(json& j, const CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t& data) {
const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
}

View File

@@ -0,0 +1,126 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file download_action_list_command.hpp
* @brief Download action list command
**/
#ifndef _HAILO_DOWNLOAD_ACTION_LIST_COMMAND_HPP_
#define _HAILO_DOWNLOAD_ACTION_LIST_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "context_switch_defs.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using ordered_json = nlohmann::ordered_json;
class DownloadActionListCommand : public DeviceCommand
{
public:
explicit DownloadActionListCommand(CLI::App &parent_app);
// To be used from external commands
static hailo_status execute(Device &device, const std::string &output_file_path,
const ConfiguredNetworkGroupVector &network_groups={}, const std::string &hef_file_path="");
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_output_file_path;
static constexpr int DEFAULT_JSON_TAB_WIDTH = 4;
static constexpr int INVALID_NUMERIC_VALUE = -1;
static std::string ACTION_LIST_FORMAT_VERSION() { return "1.0"; }
static Expected<ordered_json> parse_hef_metadata(const std::string &hef_file_path);
static bool is_valid_hef(const std::string &hef_file_path);
static Expected<std::string> calc_md5_hexdigest(const std::string &hef_file_path);
static hailo_status write_json(const ordered_json &json_obj, const std::string &output_file_path,
int tab_width = DEFAULT_JSON_TAB_WIDTH);
static Expected<ordered_json> parse_action_data(uint32_t base_address, uint8_t *action,
uint32_t current_buffer_offset, uint32_t *action_length, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type,
uint32_t timestamp, uint8_t sub_action_index = 0, bool sub_action_index_set = false,
bool *is_repeated = nullptr, uint8_t *num_repeated = nullptr,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type = nullptr);
static Expected<ordered_json> parse_single_repeated_action(uint32_t base_address, uint8_t *action,
uint32_t current_buffer_offset, uint32_t *action_length, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type,
uint32_t timestamp, uint8_t index_in_repeated_block);
static Expected<ordered_json> parse_single_action(uint32_t base_address, uint8_t *context_action_list,
uint32_t current_buffer_offset, uint32_t *action_length, bool *is_repeated, uint8_t *num_repeated,
CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type, uint32_t *time_stamp);
static Expected<ordered_json> parse_context(uint32_t action_list_base_address, uint8_t *context_action_list,
uint16_t action_list_size, uint32_t batch_counter);
static double get_accumulator_mean_value(const AccumulatorPtr &accumulator, double default_value = INVALID_NUMERIC_VALUE);
static Expected<ordered_json> parse_network_groups(Device &device, const ConfiguredNetworkGroupVector &network_groups);
};
// JSON serialization
NLOHMANN_JSON_SERIALIZE_ENUM(CONTEXT_SWITCH_DEFS__ACTION_TYPE_t, {
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS, "fetch_vdma_descriptors"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_TRIGGER_SEQUENCER, "trigger_sequencer"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_DATA_FROM_VDMA_CHANNEL, "fetch_data_from_vdma_channel"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT, "enable_lcu_default"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_NON_DEFAULT, "enable_lcu_non_default"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_DISABLE_LCU, "disable_lcu"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_INPUT, "activate_boundary_input"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_OUTPUT, "activate_boundary_output"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_INPUT, "activate_inter_context_input"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_OUTPUT, "activate_inter_context_output"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_INPUT, "activate_ddr_buffer_input"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_OUTPUT, "activate_ddr_buffer_output"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_VDMA_CHANNEL, "deactivate_vdma_channel"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_CHANGE_VDMA_TO_STREAM_MAPPING, "change_vdma_to_stream_mapping"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ADD_DDR_PAIR_INFO, "add_ddr_pair_info"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_DDR_BUFFERING_START, "ddr_buffering_start"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_LCU_INTERRUPT, "lcu_interrupt"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_SEQUENCER_DONE_INTERRUPT, "sequencer_done_interrupt"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_INPUT_CHANNEL_TRANSFER_DONE_INTERRUPT, "input_channel_transfer_done_interrupt"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_OUTPUT_CHANNEL_TRANSFER_DONE_INTERRUPT, "output_channel_transfer_done_interrupt"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_MODULE_CONFIG_DONE_INTERRUPT, "module_config_done_interrupt"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_APPLICATION_CHANGE_INTERRUPT, "application_change_interrupt"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL, "activate_cfg_channel"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_CFG_CHANNEL, "deactivate_cfg_channel"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION, "repeated_action"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_DMA_IDLE_ACTION, "wait_for_dma_idle_action"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_NMS_IDLE, "wait_for_nms_idle"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS, "fetch_ccw_bursts"},
{CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT, nullptr},
});
// Default implementions
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__repeated_action_header_t, count, last_executed, sub_action_type);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t, cluster_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t, vdma_channel_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__fetch_data_action_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t, h2d_vdma_channel_index, d2h_vdma_channel_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t, vdma_channel_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t, sequencer_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t, aggregator_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t, module_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t, application_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t, vdma_channel_index, stream_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_cfg_channel_t, channel_index);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t, channel_index);
// Non-default implementations
void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t& data);
void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t& data);
void to_json(json& j, const CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t& data);
void to_json(json& j, const CONTEXT_SWITCH_DEFS__read_vdma_action_data_t& data);
void to_json(json& j, const CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t& data);
void to_json(json& j, const CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t& data);
void to_json(json& j, const CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t& data);
#endif /* _HAILO_DOWNLOAD_ACTION_LIST_COMMAND_HPP_ */

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_config_command.cpp
* @brief User Firmware configuration (persistent config) command.
**/
#include "fw_config_command.hpp"
FwConfigReadSubcommand::FwConfigReadSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("read", "Read firmware configuration from device"))
{
m_app->add_option("--output-file", m_output_file, "File path to write user firmware configuration into.\n"
"If not given the data will be printed to stdout.");
}
hailo_status FwConfigReadSubcommand::execute_on_device(Device &device)
{
auto user_config_buffer = device.read_user_config();
CHECK_EXPECTED_AS_STATUS(user_config_buffer, "Failed reading user config from device");
auto status = FwConfigJsonSerializer::deserialize_config(
*reinterpret_cast<USER_CONFIG_header_t*>(user_config_buffer->data()),
user_config_buffer->size(), m_output_file);
CHECK_SUCCESS(status);
return HAILO_SUCCESS;
}
FwConfigWriteSubcommand::FwConfigWriteSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("write", "Write firmware configuration to device"))
{
m_app->add_option("input_file", m_input_file, "User firmware configuration file path.")
->check(CLI::ExistingFile)
->required();
}
hailo_status FwConfigWriteSubcommand::execute_on_device(Device &device)
{
auto config_buffer = Buffer::create(FLASH_USER_CONFIG_SECTION_SIZE);
CHECK_EXPECTED_AS_STATUS(config_buffer);
auto config_size = FwConfigJsonSerializer::serialize_config(
*reinterpret_cast<USER_CONFIG_header_t*>(config_buffer->data()), config_buffer->size(), m_input_file);
CHECK_EXPECTED_AS_STATUS(config_size);
// We only need to write config_size.value() bytes from config_buffer, so we "resize" the buffer
CHECK(config_buffer->size() >= config_size.value(), HAILO_INTERNAL_FAILURE,
"Unexpected config size {} (max_size={})", config_size.value(), config_buffer->size());
auto resized_config_buffer = Buffer::create(config_buffer->data(), config_size.value());
CHECK_EXPECTED_AS_STATUS(resized_config_buffer);
hailo_status status = device.write_user_config(MemoryView(resized_config_buffer.value()));
CHECK_SUCCESS(status, "Failed writing user firmware configuration to device");
return HAILO_SUCCESS;
}
FwConfigSerializeSubcommand::FwConfigSerializeSubcommand(CLI::App &parent_app) :
Command(parent_app.add_subcommand("serialize", "Serialize firmware configuration json to a binary file"))
{
m_app->add_option("input_file", m_input_file, "File path to firmware configuration json")
->check(CLI::ExistingFile)
->required();
m_app->add_option("output_file", m_output_file, "File path to write binary firmware configuration into")
->required();
}
hailo_status FwConfigSerializeSubcommand::execute()
{
auto config_buffer = Buffer::create(FLASH_USER_CONFIG_SECTION_SIZE);
CHECK_EXPECTED_AS_STATUS(config_buffer);
USER_CONFIG_header_t *config_header = reinterpret_cast<USER_CONFIG_header_t*>(config_buffer->data());
auto config_size = FwConfigJsonSerializer::serialize_config(*config_header, config_buffer->size(), m_input_file);
CHECK_EXPECTED_AS_STATUS(config_size);
std::ofstream ofs(m_output_file, std::ios::out | std::ios::binary);
CHECK(ofs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening file: {}, with errno: {}", m_output_file, errno);
ofs.write(reinterpret_cast<char*>(config_header), config_size.value());
CHECK(ofs.good(), HAILO_FILE_OPERATION_FAILURE,
"Failed writing binary firmware configuration to file: {}, with errno: {}", m_output_file, errno);
return HAILO_SUCCESS;
}
FwConfigCommand::FwConfigCommand(CLI::App &parent_app) :
ContainerCommand(parent_app.add_subcommand("fw-config", "User firmware configuration tool"))
{
add_subcommand<FwConfigReadSubcommand>();
add_subcommand<FwConfigWriteSubcommand>();
add_subcommand<FwConfigSerializeSubcommand>();
}

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_config_command.hpp
* @brief User firmware configuration command.
**/
#ifndef _HAILO_FW_CONFIG_COMMAND_HPP_
#define _HAILO_FW_CONFIG_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "fw_config_serializer.hpp"
#include "hailo/device.hpp"
#include "CLI/CLI.hpp"
#define FLASH_USER_CONFIG_SECTION_SIZE (0x008000)
class FwConfigReadSubcommand final : public DeviceCommand {
public:
explicit FwConfigReadSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_output_file;
};
class FwConfigWriteSubcommand final : public DeviceCommand {
public:
explicit FwConfigWriteSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_input_file;
};
class FwConfigSerializeSubcommand final : public Command {
public:
explicit FwConfigSerializeSubcommand(CLI::App &parent_app);
hailo_status execute() override;
private:
std::string m_input_file;
std::string m_output_file;
};
class FwConfigCommand final : public ContainerCommand {
public:
explicit FwConfigCommand(CLI::App &parent_app);
};
#endif /* _HAILO_FW_CONFIG_COMMAND_HPP_ */

View File

@@ -0,0 +1,904 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_config_serializer.cpp
* @brief User firmware configuration serializer.
**/
#include "fw_config_serializer.hpp"
#include "definitions_json.auto.hpp"
#include "hailo/hailort.h"
#include "user_config_common.h"
#include "common/file_utils.hpp"
#include <fstream>
#include <sstream>
#include <iomanip>
Expected<ordered_json> FwConfigJsonSerializer::read_json_file(const std::string &file_path)
{
std::ifstream ifs(file_path);
CHECK_AS_EXPECTED(ifs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening json file: {} with errno: {}", file_path, errno);
ordered_json j;
ifs >> j;
CHECK_AS_EXPECTED(ifs.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading json file {}", file_path);
return j;
}
Expected<std::map<std::string, std::map<std::string, ordered_json>>> FwConfigJsonSerializer::get_serialize_map()
{
ordered_json json_definitions = ordered_json::parse(g_fw_config_str_definitions);
std::map<std::string, std::map<std::string, ordered_json>> definitions;
auto version = json_definitions.find("version");
CHECK_AS_EXPECTED(version != json_definitions.end(), HAILO_INTERNAL_FAILURE,
"Failed to find version in definitions file");
definitions["version"]["value"] = version.value();
auto categories = json_definitions.find("categories");
CHECK_AS_EXPECTED(categories != json_definitions.end(), HAILO_INTERNAL_FAILURE,
"Failed to find categories in definitions file");
uint32_t category_id = 0;
for (auto &category : categories->items()) {
auto entries = category.value().find("entries");
CHECK_AS_EXPECTED(entries != category.value().end(), HAILO_INTERNAL_FAILURE,
"Failed to find entries of category {} in definition file", category.key());
std::map<std::string, ordered_json> entries_map;
uint32_t entry_id = 0;
for (auto &entry : entries.value().items()) {
entry.value()["category_id"] = category_id;
entry.value()["entry_id"] = entry_id;
entries_map[entry.key()] = entry.value();
entry_id++;
}
definitions[category.key()] = entries_map;
category_id++;
}
return definitions;
}
Expected<std::vector<std::vector<ordered_json>>> FwConfigJsonSerializer::get_deserialize_vector()
{
ordered_json json_definitions = ordered_json::parse(g_fw_config_str_definitions);
auto categories = json_definitions.find("categories");
CHECK_AS_EXPECTED(categories != json_definitions.end(), HAILO_INTERNAL_FAILURE,
"Failed to find categories in definitions file");
std::vector<std::vector<ordered_json>> categories_vector;
for (auto &category : categories.value().items()) {
auto entries = category.value().find("entries");
CHECK_AS_EXPECTED(entries != category.value().end(), HAILO_INTERNAL_FAILURE,
"Failed to find entries of category {} in definitions file", category.key());
std::vector<ordered_json> entries_vector;
for (auto &entry : entries.value().items()) {
entry.value()["category_name"] = category.key();
entry.value()["entry_name"] = entry.key();
entries_vector.emplace_back(entry.value());
}
categories_vector.emplace_back(entries_vector);
}
return categories_vector;
}
hailo_status FwConfigJsonSerializer::dump_config(const ordered_json &config_json, const std::string &file_path)
{
if (file_path.empty()) {
std::cout << config_json.dump(JSON_PRINT_INDENTATION) << std::endl;
CHECK(std::cout.good(), HAILO_FILE_OPERATION_FAILURE, "Failed writing config to stdout");
}
else {
std::ofstream ofs(file_path);
CHECK(ofs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening output file: {} with errno: {}", file_path, errno);
ofs << config_json.dump(JSON_PRINT_INDENTATION) << std::endl;
CHECK(ofs.good(), HAILO_FILE_OPERATION_FAILURE, "Failed writing firmware configuration into file: {} with errno: {}", file_path, errno);
}
return HAILO_SUCCESS;
}
/* Deserialization */
hailo_status FwConfigJsonSerializer::deserialize_config(const USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path)
{
try {
auto categories = get_deserialize_vector();
CHECK_EXPECTED_AS_STATUS(categories);
ordered_json config_json;
size_t current_deserialized_data_size = 0;
uintptr_t current_entry_offset = (uintptr_t)(&(user_config_header.entries));
for (size_t i = 0; i < user_config_header.entry_count; i++) {
USER_CONFIG_ENTRY_t *config_entry = reinterpret_cast<USER_CONFIG_ENTRY_t*>(current_entry_offset);
CHECK(config_entry->category < categories->size(), HAILO_INTERNAL_FAILURE,
"Category id is out of bounds. Category id = {}, Max category id = {}", config_entry->category, (categories->size()-1));
auto category = categories.value()[config_entry->category];
CHECK(config_entry->entry_id < category.size(), HAILO_INTERNAL_FAILURE,
"Entry id is out of bounds. Entry id = {}, Max entry id = {}", config_entry->entry_id, (category.size() - 1));
current_deserialized_data_size += sizeof(USER_CONFIG_ENTRY_t) + config_entry->entry_size;
CHECK((current_deserialized_data_size <= config_size), HAILO_INVALID_OPERATION,
"Overrun configuration size. Deserialized data size = {}, configuration data size is: {}", current_deserialized_data_size, config_size);
hailo_status status = FwConfigJsonSerializer::deserialize_entry(config_json,
category[config_entry->entry_id], reinterpret_cast<uint8_t*>(&(config_entry->value)));
CHECK_SUCCESS(status);
current_entry_offset += sizeof(USER_CONFIG_ENTRY_t) + config_entry->entry_size;
}
hailo_status status = dump_config(config_json, file_path);
CHECK_SUCCESS(status, "Failed writing firmware configuration");
}
catch (json::exception &e) {
LOGGER__ERROR("Exception caught: {}.\n Please check json definition file format", e.what());
return HAILO_INTERNAL_FAILURE;
}
return HAILO_SUCCESS;
}
hailo_status FwConfigJsonSerializer::deserialize_entry(ordered_json &config_json, const ordered_json &entry_definition, uint8_t *entry_value)
{
std::string category_name = entry_definition["category_name"].get<std::string>();
std::string entry_name = entry_definition["entry_name"].get<std::string>();
std::string deserialize_as = entry_definition["deserialize_as"].get<std::string>();
uint32_t size = entry_definition.contains("length") ?
(entry_definition["length"].get<uint32_t>() * entry_definition["size"].get<uint32_t>()) :
entry_definition["size"].get<uint32_t>();
if (deserialize_as == "str") {
auto str_val = deserialize_str(entry_value, size);
CHECK_EXPECTED_AS_STATUS(str_val);
config_json[category_name][entry_name] = str_val.value();
}
else if (deserialize_as == "bool") {
auto bool_val = deserialize_bool(entry_value, size);
CHECK_EXPECTED_AS_STATUS(bool_val);
config_json[category_name][entry_name] = bool_val.value();
}
else if (deserialize_as == "int") {
auto int_val = deserialize_int(entry_value, size);
CHECK_EXPECTED_AS_STATUS(int_val);
config_json[category_name][entry_name] = int_val.value();
}
else if (deserialize_as == "i2c_speed") {
auto i2c_speed_val = deserialize_i2c_speed(entry_value, size);
CHECK_EXPECTED_AS_STATUS(i2c_speed_val);
config_json[category_name][entry_name]["value"] = i2c_speed_val.value();
}
else if (deserialize_as == "supported_aspm_states") {
auto supported_aspm_states_val = deserialize_supported_aspm_states(entry_value, size);
CHECK_EXPECTED_AS_STATUS(supported_aspm_states_val);
config_json[category_name][entry_name]["value"] = supported_aspm_states_val.value();
}
else if (deserialize_as == "supported_aspm_l1_substates") {
auto supported_aspm_l1_substates_val = deserialize_supported_aspm_l1_substates(entry_value, size);
CHECK_EXPECTED_AS_STATUS(supported_aspm_l1_substates_val);
config_json[category_name][entry_name]["value"] = supported_aspm_l1_substates_val.value();
}
else if (deserialize_as == "ipv4") {
auto ipv4_val = deserialize_ipv4(entry_value, size);
CHECK_EXPECTED_AS_STATUS(ipv4_val);
config_json[category_name][entry_name]["value"] = ipv4_val.value();
}
else if (deserialize_as == "mac_address") {
auto mac_address_val = deserialize_mac_address(entry_value, size);
CHECK_EXPECTED_AS_STATUS(mac_address_val);
config_json[category_name][entry_name]["value"] = mac_address_val.value();
}
else if (deserialize_as == "clock_frequency") {
auto clock_frequency_val = deserialize_clock_frequency(entry_value, size);
CHECK_EXPECTED_AS_STATUS(clock_frequency_val);
config_json[category_name][entry_name]["value"] = clock_frequency_val.value();
}
else if (deserialize_as == "logger_level") {
auto logger_level = deserialize_logger_level(entry_value, size);
CHECK_EXPECTED_AS_STATUS(logger_level);
config_json[category_name][entry_name]["value"] = logger_level.value();
}
else if (deserialize_as == "watchdog_mode") {
auto watchdog_mode_val = deserialize_watchdog_mode(entry_value, size);
CHECK_EXPECTED_AS_STATUS(watchdog_mode_val);
config_json[category_name][entry_name]["value"] = watchdog_mode_val.value();
}
else if (deserialize_as == "overcurrent_parameters_source") {
auto overcurrent_parameters_source_val = deserialize_overcurrent_parameters_source(entry_value, size);
CHECK_EXPECTED_AS_STATUS(overcurrent_parameters_source_val);
config_json[category_name][entry_name]["value"] = overcurrent_parameters_source_val.value();
}
else if (deserialize_as == "temperature_parameters_source") {
auto temperature_parameters_source_val = deserialize_temperature_parameters_source(entry_value, size);
CHECK_EXPECTED_AS_STATUS(temperature_parameters_source_val);
config_json[category_name][entry_name]["value"] = temperature_parameters_source_val.value();
}
else if (deserialize_as == "conversion_time") {
auto conversion_time_val = deserialize_conversion_time(entry_value, size);
CHECK_EXPECTED_AS_STATUS(conversion_time_val);
config_json[category_name][entry_name]["value"] = conversion_time_val.value();
}
else {
LOGGER__ERROR("Failed deserializing entry. Serialization format {} not found", deserialize_as);
return HAILO_NOT_FOUND;
}
return HAILO_SUCCESS;
}
Expected<json> FwConfigJsonSerializer::deserialize_str(uint8_t *entry_value, uint32_t size)
{
return json(std::string((char*)entry_value, strnlen((char*)entry_value, size)));
}
Expected<json> FwConfigJsonSerializer::deserialize_bool(uint8_t *entry_value, uint32_t size)
{
auto bool_val = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(bool_val);
json bool_str = bool_val.value() ? true : false;
return bool_str;
}
Expected<json> FwConfigJsonSerializer::deserialize_ipv4(uint8_t *entry_value, uint32_t size)
{
CHECK_AS_EXPECTED((IPV4_ADDRESS_LENGTH == size), HAILO_INTERNAL_FAILURE,
"IPv4 address length is invalid. Length recieved is: {}, length expected: {}", size, IPV4_ADDRESS_LENGTH);
std::stringstream ss;
for (size_t i = 0; i < IPV4_ADDRESS_LENGTH; i++) {
if (i != 0) {
ss << '.';
}
//TODO: HRT-3045 - Support big-endian.
ss << (int)(entry_value[IPV4_ADDRESS_LENGTH-i-1]);
}
return json(ss.str());
}
Expected<json> FwConfigJsonSerializer::deserialize_mac_address(uint8_t *entry_value, uint32_t size)
{
CHECK_AS_EXPECTED((MAC_ADDRESS_LENGTH == size), HAILO_INTERNAL_FAILURE,
"Mac address length is invalid. Length recieved is: {}, length expected: {}", size, MAC_ADDRESS_LENGTH);
std::stringstream ss;
for (size_t i = 0; i < MAC_ADDRESS_LENGTH; i++) {
if (i != 0) {
ss << ':';
}
ss.width(2);
ss.fill('0');
ss << std::uppercase << std::hex << (int)(entry_value[i]);
}
return json(ss.str());
}
Expected<json> FwConfigJsonSerializer::deserialize_supported_aspm_states(uint8_t *entry_value, uint32_t size)
{
auto aspm_state = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(aspm_state);
switch (static_cast<PCIE_CONFIG_SUPPOPRTED_ASPM_STATES_t>(aspm_state.value())) {
case ASPM_DISABLED:
return json("ASPM DISABLED");
case ASPM_L1_ONLY:
return json("ASPM L1 ONLY");
case ASPM_L0S_L1:
return json("ASPM L0S L1");
default:
LOGGER__ERROR("Failed deserializing supported aspm states.");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_supported_aspm_l1_substates(uint8_t *entry_value, uint32_t size)
{
auto aspm_l1_substate = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(aspm_l1_substate);
switch (static_cast<PCIE_CONFIG_SUPPOPRTED_L1_ASPM_SUBSTATES_t>(aspm_l1_substate.value())) {
case ASPM_L1_SUBSTATES_DISABLED:
return json("ASPM L1 SUBSTATES DISABLED");
case ASPM_L1_SUBSTATES_L11_ONLY:
return json("ASPM L1 SUBSTATES L1.1 ONLY");
case ASPM_L1_SUBSTATES_L11_L12:
return json("ASPM L1 SUBSTATES L1.1 L1.2");
default:
LOGGER__ERROR("Failed deserializing supported aspm l1 substates.");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_clock_frequency(uint8_t *entry_value, uint32_t size)
{
auto clock_frequency = get_int_value<uint32_t>(entry_value, size);
CHECK_EXPECTED(clock_frequency);
switch (clock_frequency.value()) {
case SOC__NN_CLOCK_400MHz:
return json("400MHZ");
case SOC__NN_CLOCK_375MHz:
return json("375MHZ");
case SOC__NN_CLOCK_350MHz:
return json("350MHZ");
case SOC__NN_CLOCK_325MHz:
return json("325MHZ");
case SOC__NN_CLOCK_300MHz:
return json("300MHZ");
case SOC__NN_CLOCK_275MHz:
return json("275MHZ");
case SOC__NN_CLOCK_250MHz:
return json("250MHZ");
case SOC__NN_CLOCK_225MHz:
return json("225MHZ");
case SOC__NN_CLOCK_200MHz:
return json("200MHZ");
case SOC__NN_CLOCK_100MHz:
return json("100MHZ");
default:
LOGGER__ERROR("Failed deserializing clock_frequency.");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_watchdog_mode(uint8_t *entry_value, uint32_t size)
{
auto watchdog_mode = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(watchdog_mode);
switch (static_cast<WD_SERVICE_wd_mode_t>(watchdog_mode.value())) {
case WD_SERVICE_MODE_HW_SW:
return json("WD MODE HW SW");
case WD_SERVICE_MODE_HW_ONLY:
return json("WD MODE HW ONLY");
default:
LOGGER__ERROR("Failed deserializing watchdog_mode.");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_i2c_speed(uint8_t *entry_value, uint32_t size)
{
auto i2c_speed = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(i2c_speed);
switch (static_cast<i2c_speed_mode_t>(i2c_speed.value())) {
case I2C_SPEED_STANDARD:
return json("I2C SPEED STANDARD");
case I2C_SPEED_FAST:
return json("I2C SPEED FAST");
default:
LOGGER__ERROR("Failed deserializing i2c speed");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_logger_level(uint8_t *entry_value, uint32_t size)
{
auto logger_level = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(logger_level);
switch (static_cast<FW_LOGGER_LEVEL_t>(logger_level.value())) {
case FW_LOGGER_LEVEL_TRACE:
return json("TRACE");
case FW_LOGGER_LEVEL_DEBUG:
return json("DEBUG");
case FW_LOGGER_LEVEL_INFO:
return json("INFO");
case FW_LOGGER_LEVEL_WARN:
return json("WARNING");
case FW_LOGGER_LEVEL_ERROR:
return json("ERROR");
case FW_LOGGER_LEVEL_FATAL:
return json("FATAL");
default:
LOGGER__ERROR("Failed deserializing logger_level");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_overcurrent_parameters_source(uint8_t *entry_value, uint32_t size)
{
auto overcurrent_parameters_source = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(overcurrent_parameters_source);
switch (static_cast<OVERCURRENT_parameters_source_t>(overcurrent_parameters_source.value())) {
case OVERCURRENT_PARAMETERS_SOURCE_FW_VALUES:
return json("FW VALUES");
case OVERCURRENT_PARAMETERS_SOURCE_USER_CONFIG_VALUES:
return json("USER CONFIG VALUES");
case OVERCURRENT_PARAMETERS_SOURCE_BOARD_CONFIG_VALUES:
return json("BOARD CONFIG VALUES");
case OVERCURRENT_PARAMETERS_SOURCE_OVERCURRENT_DISABLED:
return json("OVERCURRENT DISABLED");
default:
LOGGER__ERROR("Failed deserializing overcurrent thresholds source");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_temperature_parameters_source(uint8_t *entry_value, uint32_t size)
{
auto temperature_parameters_source = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(temperature_parameters_source);
switch (static_cast<TEMPERATURE_PROTECTION_parameters_source_t>(temperature_parameters_source.value())) {
case TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_FW_VALUES:
return json("FW VALUES");
case TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_USER_CONFIG_VALUES:
return json("USER CONFIG VALUES");
default:
LOGGER__ERROR("Failed deserializing overcurrent thresholds source");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_conversion_time(uint8_t *entry_value, uint32_t size)
{
auto conversion_time = get_int_value<uint32_t>(entry_value, size);
CHECK_EXPECTED(conversion_time);
auto conversion_time_value = static_cast<OVERCURRENT_conversion_time_us_t>(conversion_time.value());
if (conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_140US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_204US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_332US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_588US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_1100US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_2116US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_4156US ||
conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_8244US) {
return json(conversion_time_value);
}
else {
LOGGER__ERROR("Got unvalid valid option for conversion_time.");
return make_unexpected(HAILO_NOT_FOUND);
}
}
Expected<json> FwConfigJsonSerializer::deserialize_int(uint8_t *entry_value, uint32_t size)
{
switch (size) {
case sizeof(uint8_t):
{
auto uint8_val = get_int_value<uint8_t>(entry_value, size);
CHECK_EXPECTED(uint8_val);
return json(uint8_val.value());
}
case sizeof(uint16_t):
{
auto uint16_val = get_int_value<uint16_t>(entry_value, size);
CHECK_EXPECTED(uint16_val);
return json(uint16_val.value());
}
case sizeof(uint32_t):
{
auto uint32_val = get_int_value<uint32_t>(entry_value, size);
CHECK_EXPECTED(uint32_val);
return json(uint32_val.value());
}
default:
LOGGER__ERROR("Failed deserializing int value");
return make_unexpected(HAILO_NOT_FOUND);
}
}
/* Serialization */
Expected<uint32_t> FwConfigJsonSerializer::serialize_config(USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path)
{
size_t data_size = sizeof(USER_CONFIG_header_t);
try {
auto config_json = FwConfigJsonSerializer::read_json_file(file_path);
CHECK_EXPECTED(config_json);
auto definitions = FwConfigJsonSerializer::get_serialize_map();
CHECK_EXPECTED(definitions);
user_config_header.version = definitions.value()["version"]["value"].get<uint32_t>();
user_config_header.magic = USER_CONFIG_MAGIC;
user_config_header.entry_count = 0;
uintptr_t current_entry_offset = (uintptr_t)(&(user_config_header.entries));
for (auto &config_category : config_json->items()) {
for (auto &config_entry : config_category.value().items()) {
ordered_json entry_definition = definitions.value()[config_category.key()][config_entry.key()];
USER_CONFIG_ENTRY_t *curr_entry = (USER_CONFIG_ENTRY_t *)current_entry_offset;
curr_entry->entry_size = entry_definition.contains("length") ?
(entry_definition["length"].get<uint32_t>() * entry_definition["size"].get<uint32_t>()) :
entry_definition["size"].get<uint32_t>();
data_size += sizeof(USER_CONFIG_ENTRY_t) + curr_entry->entry_size;
CHECK_AS_EXPECTED((data_size <= config_size), HAILO_INVALID_OPERATION,
"User config overrun! Configuration is too big, data size {}, firmware configuration size: {}",
data_size, config_size);
hailo_status status = serialize_entry(*curr_entry, config_entry.value(), entry_definition);
CHECK_SUCCESS_AS_EXPECTED(status, "Failed serializing json config category: {}, entry: {}",
config_category.key(), config_entry.key());
user_config_header.entry_count++;
current_entry_offset += sizeof(USER_CONFIG_ENTRY_t) + curr_entry->entry_size;
}
}
}
catch (json::exception &e) {
LOGGER__ERROR("Exception caught: {}.\n Please check json files format", e.what());
return make_unexpected(HAILO_INTERNAL_FAILURE);
}
CHECK_AS_EXPECTED(((std::numeric_limits<uint32_t>::min() <= data_size) && (data_size <= std::numeric_limits<uint32_t>::max())),
HAILO_INTERNAL_FAILURE, "Firmware configuration data size is out of bounds. data_size = {}", data_size);
return static_cast<uint32_t>(data_size);
}
hailo_status FwConfigJsonSerializer::serialize_entry(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry, const ordered_json &entry_definition)
{
ordered_json inner_config_entry = config_entry;
entry.category = entry_definition["category_id"].get<uint16_t>();
entry.entry_id = entry_definition["entry_id"].get<uint16_t>();
std::string deserialize_as = entry_definition["deserialize_as"].get<std::string>();
if (deserialize_as == "str") {
return serialize_str(entry, inner_config_entry);
}
else if (deserialize_as == "bool") {
return serialize_bool(entry, inner_config_entry);
}
else if (deserialize_as == "int") {
return serialize_int_by_size(entry, inner_config_entry);
}
else {
if (!config_entry.contains("value"))
{
inner_config_entry = {{"value", config_entry}};
}
if (deserialize_as == "ipv4") {
return serialize_ipv4(entry, inner_config_entry);
}
else if (deserialize_as == "i2c_speed") {
return serialize_i2c_speed(entry, inner_config_entry);
}
else if (deserialize_as == "supported_aspm_states") {
return serialize_supported_aspm_states(entry, inner_config_entry);
}
else if (deserialize_as == "supported_aspm_l1_substates") {
return serialize_supported_aspm_l1_substates(entry, inner_config_entry);
}
else if (deserialize_as == "mac_address") {
return serialize_mac_address(entry, inner_config_entry);
}
else if (deserialize_as == "clock_frequency") {
return serialize_clock_frequency(entry, inner_config_entry);
}
else if (deserialize_as == "logger_level") {
return serialize_logger_level(entry, inner_config_entry);
}
else if (deserialize_as == "watchdog_mode") {
return serialize_watchdog_mode(entry, inner_config_entry);
}
else if (deserialize_as == "overcurrent_parameters_source") {
return serialize_overcurrent_parameters_source(entry, inner_config_entry);
}
else if (deserialize_as == "temperature_parameters_source") {
return serialize_temperature_parameters_source(entry, inner_config_entry);
}
else if (deserialize_as == "conversion_time") {
return serialize_conversion_time(entry, inner_config_entry);
}
else {
LOGGER__ERROR("Failed serializing entry. Serialization format {} not found", deserialize_as);
return HAILO_NOT_FOUND;
}
}
}
hailo_status FwConfigJsonSerializer::serialize_str(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string str = config_entry.get<std::string>();
CHECK(0 != str.length(), HAILO_INVALID_ARGUMENT, "Failed serializing string. String length can't be 0.");
CHECK(entry.entry_size >= str.length(), HAILO_INVALID_ARGUMENT,
"Failed serializing string value {}. String length must be equal or shorter than {}", str, entry.entry_size);
memcpy(&(entry.value), str.c_str(), str.length());
return HAILO_SUCCESS;
}
hailo_status FwConfigJsonSerializer::serialize_bool(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
uint8_t bool_value = config_entry.get<uint8_t>();
return serialize_int(entry, bool_value);
}
hailo_status FwConfigJsonSerializer::serialize_ipv4(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
CHECK(IPV4_ADDRESS_LENGTH == entry.entry_size, HAILO_INVALID_ARGUMENT,
"IPv4 entry size is incompatible. Size recieved: {}, size expected: {}", entry.entry_size, IPV4_ADDRESS_LENGTH);
std::string ipv4_str = config_entry["value"].get<std::string>();
uint8_t ip[IPV4_ADDRESS_LENGTH];
auto length = sscanf(ipv4_str.c_str(), "%hhd.%hhd.%hhd.%hhd", &ip[3], &ip[2], &ip[1], &ip[0]);
CHECK(IPV4_ADDRESS_LENGTH == length, HAILO_INVALID_ARGUMENT, "Failed serializing ipv4: {}", ipv4_str);
memcpy(&(entry.value), &ip, entry.entry_size);
return HAILO_SUCCESS;
}
hailo_status FwConfigJsonSerializer::serialize_i2c_speed(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string i2c_speed = config_entry["value"].get<std::string>();
uint8_t val = 0;
if (("I2C_SPEED_STANDARD" == i2c_speed) || ("I2C SPEED STANDARD" == i2c_speed)) {
val = I2C_SPEED_STANDARD;
}
else if (("I2C_SPEED_FAST" == i2c_speed) || ("I2C SPEED FAST" == i2c_speed)) {
val = I2C_SPEED_FAST;
}
else {
LOGGER__ERROR("Failed serializing i2c speed {}", i2c_speed);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_logger_level(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string logger_level = config_entry["value"].get<std::string>();
uint8_t val = 0;
if ("TRACE" == logger_level) {
val = FW_LOGGER_LEVEL_TRACE;
}
else if ("DEBUG" == logger_level) {
val = FW_LOGGER_LEVEL_DEBUG;
}
else if ("INFO" == logger_level) {
val = FW_LOGGER_LEVEL_INFO;
}
else if ("WARNING" == logger_level) {
val = FW_LOGGER_LEVEL_WARN;
}
else if ("ERROR" == logger_level) {
val = FW_LOGGER_LEVEL_ERROR;
}
else if ("FATAL" == logger_level) {
val = FW_LOGGER_LEVEL_FATAL;
}
else {
LOGGER__ERROR("Failed serializing logger_level {}", logger_level);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_supported_aspm_states(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string aspm_state = config_entry["value"].get<std::string>();
uint8_t val = 0;
if (("ASPM_DISABLED" == aspm_state) || ("ASPM DISABLED" == aspm_state)) {
val = ASPM_DISABLED;
}
else if (("ASPM_L1_ONLY" == aspm_state) || ("ASPM L1 ONLY" == aspm_state)) {
val = ASPM_L1_ONLY;
}
else if (("ASPM_L0S_L1" == aspm_state) || ("ASPM L0S L1" == aspm_state)) {
val = ASPM_L0S_L1;
}
else {
LOGGER__ERROR("Failed serializing supported aspm state {}.", aspm_state);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_supported_aspm_l1_substates(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string aspm_l1_substate = config_entry["value"].get<std::string>();
uint8_t val = 0;
if (("ASPM_L1_SUBSTATES_DISABLED" == aspm_l1_substate) || ("ASPM L1 SUBSTATES DISABLED" == aspm_l1_substate)) {
val = ASPM_L1_SUBSTATES_DISABLED;
}
else if (("ASPM_L1_SUBSTATES_L11_ONLY" == aspm_l1_substate) || ("ASPM L1 SUBSTATES L1.1 ONLY" == aspm_l1_substate)) {
val = ASPM_L1_SUBSTATES_L11_ONLY;
}
else if (("ASPM_L1_SUBSTATES_L11_L12" == aspm_l1_substate) || ("ASPM L1 SUBSTATES L1.1 L1.2" == aspm_l1_substate)) {
val = ASPM_L1_SUBSTATES_L11_L12;
}
else {
LOGGER__ERROR("Failed serializing supported aspm l1 substate {}", aspm_l1_substate);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_mac_address(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string mac_addr_str = config_entry["value"].get<std::string>();
uint8_t mac_address[MAC_ADDRESS_LENGTH];
auto length = std::sscanf(mac_addr_str.c_str(),
"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&mac_address[0], &mac_address[1], &mac_address[2],
&mac_address[3], &mac_address[4], &mac_address[5]);
CHECK(MAC_ADDRESS_LENGTH == length, HAILO_INVALID_ARGUMENT, "Failed serializing mac address {}", mac_addr_str);
memcpy(&(entry.value), &mac_address, entry.entry_size);
return HAILO_SUCCESS;
}
hailo_status FwConfigJsonSerializer::serialize_clock_frequency(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string clock_frequency = config_entry["value"].get<std::string>();
uint32_t val = 0;
if ("400MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_400MHz;
}
else if ("375MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_375MHz;
}
else if ("350MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_350MHz;
}
else if ("325MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_325MHz;
}
else if ("300MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_300MHz;
}
else if ("275MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_275MHz;
}
else if ("250MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_250MHz;
}
else if ("225MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_225MHz;
}
else if ("200MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_200MHz;
}
else if ("100MHZ" == clock_frequency) {
val = SOC__NN_CLOCK_100MHz;
}
else {
LOGGER__ERROR("Failed serializing clock_frequency {}", clock_frequency);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_watchdog_mode(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string watchdog_mode = config_entry["value"].get<std::string>();
uint8_t val = 0;
if (("WD_MODE_HW_SW" == watchdog_mode) || ("WD MODE HW SW" == watchdog_mode)) {
val = 0;
}
else if (("WD_MODE_HW_ONLY" == watchdog_mode) || ("WD MODE HW ONLY" == watchdog_mode)) {
val = 1;
}
else {
LOGGER__ERROR("Failed serializing watchdog_mode {}", watchdog_mode);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_overcurrent_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string overcurrent_parameters_source = config_entry["value"].get<std::string>();
uint8_t val = 0;
if (("FW_VALUES" == overcurrent_parameters_source) || ("FW VALUES" == overcurrent_parameters_source)) {
val = OVERCURRENT_PARAMETERS_SOURCE_FW_VALUES;
}
else if (("USER_CONFIG_VALUES" == overcurrent_parameters_source) || ("USER CONFIG VALUES" == overcurrent_parameters_source)) {
val = OVERCURRENT_PARAMETERS_SOURCE_USER_CONFIG_VALUES;
}
else if (("BOARD_CONFIG_VALUES" == overcurrent_parameters_source) || ("BOARD CONFIG VALUES" == overcurrent_parameters_source)) {
val = OVERCURRENT_PARAMETERS_SOURCE_BOARD_CONFIG_VALUES;
}
else if (("OVERCURRENT_DISABLED" == overcurrent_parameters_source) || ("OVERCURRENT DISABLED" == overcurrent_parameters_source)) {
val = OVERCURRENT_PARAMETERS_SOURCE_OVERCURRENT_DISABLED;
}
else {
LOGGER__ERROR("Failed serializing overcurrent_parameters_source {}", overcurrent_parameters_source);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_temperature_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
std::string temperature_parameters_source = config_entry["value"].get<std::string>();
uint8_t val = 0;
if (("FW_VALUES" == temperature_parameters_source) || ("FW VALUES" == temperature_parameters_source)) {
val = TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_FW_VALUES;
}
else if (("USER_CONFIG_VALUES" == temperature_parameters_source) || ("USER CONFIG VALUES" == temperature_parameters_source)) {
val = TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_USER_CONFIG_VALUES;
}
else {
LOGGER__ERROR("Failed serializing temperature_parameters_source {}", temperature_parameters_source);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_conversion_time(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
uint32_t conversion_time = config_entry["value"].get<uint32_t>();
uint32_t val = 0;
if (conversion_time == OVERCURRENT_CONVERSION_PERIOD_140US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_204US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_332US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_588US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_1100US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_2116US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_4156US ||
conversion_time == OVERCURRENT_CONVERSION_PERIOD_8244US) {
val = conversion_time;
}
else {
LOGGER__ERROR("Failed serializing conversion_time {}", conversion_time);
return HAILO_NOT_FOUND;
}
return serialize_int(entry, val);
}
hailo_status FwConfigJsonSerializer::serialize_int_by_size(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
{
int64_t value = config_entry.get<int64_t>();
if (sizeof(uint8_t) == entry.entry_size) {
CHECK(((std::numeric_limits<uint8_t>::min() <= value) && (value <= std::numeric_limits<uint8_t>::max())),
HAILO_INVALID_ARGUMENT, "Failed serializing uint8_t value: {}. Value is out of numeric limits", value);
return serialize_int<uint8_t>(entry, static_cast<uint8_t>(value));
}
else if (sizeof(uint16_t) == entry.entry_size) {
CHECK(((std::numeric_limits<uint16_t>::min() <= value) && (value <= std::numeric_limits<uint16_t>::max())),
HAILO_INVALID_ARGUMENT, "Failed serializing uint16_t value: {}. Value is out of numeric limits", value);
return serialize_int<uint16_t>(entry, static_cast<uint16_t>(value));
}
else if (sizeof(uint32_t) == entry.entry_size) {
CHECK(((std::numeric_limits<uint32_t>::min() <= value) && (value <= std::numeric_limits<uint32_t>::max())),
HAILO_INVALID_ARGUMENT, "Failed serializing uint32_t value: {}. Value is out of numeric limits", value);
return serialize_int<uint32_t>(entry, static_cast<uint32_t>(value));
}
else {
LOGGER__ERROR("Failed serializing int value. Invalid size {}", entry.entry_size);
return HAILO_NOT_FOUND;
}
}

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_config_serializer.hpp
* @brief User firmware configuration serializer.
**/
#ifndef _HAILO_FW_CONFIG_SERIALIZER_HPP_
#define _HAILO_FW_CONFIG_SERIALIZER_HPP_
#include "hailortcli.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "user_config_common.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using ordered_json = nlohmann::ordered_json;
#define MAC_ADDRESS_LENGTH (6)
#define IPV4_ADDRESS_LENGTH (4)
#define JSON_PRINT_INDENTATION (4)
#define USER_CONFIG_MAGIC (0x1FF6A40B)
/* NOTE: Tightly coupled with I2C_speed_mode_t defined in "i2c_handler.h"
Configuring I2c_speed_high is not supported. */
typedef enum {
I2C_SPEED_STANDARD = 1,
I2C_SPEED_FAST = 2,
} i2c_speed_mode_t;
// TODO: HRT-3045 - Add support for big-endian serialization
class FwConfigJsonSerializer {
public:
FwConfigJsonSerializer() = delete;
static Expected<ordered_json> read_json_file(const std::string &file_path);
static Expected<std::vector<std::vector<ordered_json>>> get_deserialize_vector();
static Expected<std::map<std::string, std::map<std::string, ordered_json>>> get_serialize_map();
static hailo_status dump_config(const ordered_json &config_json, const std::string &file_path);
static hailo_status deserialize_config(const USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path);
static hailo_status deserialize_entry(ordered_json &config_json, const ordered_json &entry_definition, uint8_t *entry_value);
static Expected<json> deserialize_str(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_bool(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_supported_aspm_states(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_supported_aspm_l1_substates(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_mac_address(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_i2c_speed(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_logger_level(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_ipv4(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_clock_frequency(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_watchdog_mode(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_overcurrent_parameters_source(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_temperature_parameters_source(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_conversion_time(uint8_t *entry_value, uint32_t size);
static Expected<json> deserialize_int(uint8_t *entry_value, uint32_t size);
static Expected<uint32_t> serialize_config(USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path);
static hailo_status serialize_entry(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry, const ordered_json &entry_definition);
static hailo_status serialize_str(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_bool(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_ipv4(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_i2c_speed(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_logger_level(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_supported_aspm_states(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_supported_aspm_l1_substates(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_mac_address(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_clock_frequency(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_watchdog_mode(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_overcurrent_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_temperature_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_conversion_time(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
static hailo_status serialize_int_by_size(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
template<typename IntegerType>
static Expected<IntegerType> get_int_value(uint8_t *entry_value, uint32_t size)
{
CHECK_AS_EXPECTED((sizeof(IntegerType) == size), HAILO_INTERNAL_FAILURE, "Entry size is incompatible with integer type");
auto val = *((IntegerType*)entry_value);
return val;
}
template<typename IntegerType>
static hailo_status serialize_int(USER_CONFIG_ENTRY_t &entry, IntegerType value)
{
CHECK((sizeof(IntegerType) == entry.entry_size), HAILO_INVALID_ARGUMENT,
"Entry size {} is incompatible with size of integer type {}", entry.entry_size, sizeof(IntegerType));
auto entry_value_ptr = (IntegerType*)(&entry.value);
*entry_value_ptr = value;
return HAILO_SUCCESS;
}
};
#endif /* _HAILO_FW_CONFIG_SERIALIZER_HPP_ */

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_control.cpp
* @brief Several controls that can be sent to the firware
**/
#include "fw_control_command.hpp"
#include "firmware_header_utils.h"
static const char *NOT_CONFIGURED_ATTR = "<Not Configured>";
#define MHz (1000 * 1000)
static std::string extended_device_information_boot_string(hailo_device_boot_source_t boot_source)
{
switch (boot_source) {
case HAILO_DEVICE_BOOT_SOURCE_PCIE:
return "PCIE";
case HAILO_DEVICE_BOOT_SOURCE_FLASH:
return "FLASH";
default:
return "Unknown";
}
}
static std::string extended_device_information_supported_features(hailo_device_supported_features_t supported_features)
{
std::string supported_features_str;
if(supported_features.current_monitoring) {
supported_features_str.append("Current Monitoring, ");
}
if(supported_features.ethernet) {
supported_features_str.append("Ethernet, ");
}
if(supported_features.mipi) {
supported_features_str.append("MIPI, ");
}
if(supported_features.mdio) {
supported_features_str.append("MDIO, ");
}
if(supported_features.pcie) {
supported_features_str.append("PCIE, ");
}
std::size_t last_comma_location = supported_features_str.find_last_of(",");
supported_features_str = supported_features_str.substr(0,last_comma_location);
return supported_features_str;
}
static void extended_device_information_print_array(uint8_t *array_for_print, size_t array_length, std::string splitter)
{
uint32_t i = 0;
for(i = 0; i < array_length; i++) {
std::cout << std::setfill('0') << std::setw(2) << std::uppercase << std::hex << static_cast<int>(array_for_print[i]);
if(array_length != (i+1)) {
std::cout << splitter;
}
}
std::cout << std::endl;
}
static bool extended_device_information_is_array_not_empty(uint8_t *array_for_print, size_t array_length)
{
uint32_t i = 0;
for(i = 0; i < array_length; i++) {
if(array_for_print[i] != 0){
return true;
}
}
return false;
}
static hailo_status print_extended_device_information(Device &device)
{
auto extended_info_expected = device.get_extended_device_information();
CHECK_EXPECTED_AS_STATUS(extended_info_expected, "Failed identify");
auto device_info = extended_info_expected.release();
// Print Board Extended information
std::cout << "Boot source: " << extended_device_information_boot_string(device_info.boot_source) << std::endl;
std::cout << "Neural Network Core Clock Rate: " << (device_info.neural_network_core_clock_rate/MHz) <<"MHz" <<std::endl;
std::string supported_features_str = extended_device_information_supported_features(device_info.supported_features);
if(supported_features_str.length() > 0) {
std::cout << "Device supported features: " << supported_features_str << std::endl;
}
std::cout << "LCS: " << static_cast<int>(device_info.lcs) << std::endl;
if(extended_device_information_is_array_not_empty(device_info.soc_id, sizeof(device_info.soc_id))){
std::cout << "SoC ID: ";
extended_device_information_print_array(device_info.soc_id, sizeof(device_info.soc_id), "");
}
if(extended_device_information_is_array_not_empty(device_info.eth_mac_address, sizeof(device_info.eth_mac_address))){
std::cout << "MAC Address: ";
extended_device_information_print_array(device_info.eth_mac_address, sizeof(device_info.eth_mac_address), ":");
}
if(extended_device_information_is_array_not_empty(device_info.unit_level_tracking_id, sizeof(device_info.unit_level_tracking_id))){
std::cout << "ULT ID: ";
extended_device_information_print_array(device_info.unit_level_tracking_id, sizeof(device_info.unit_level_tracking_id), "");
}
if(extended_device_information_is_array_not_empty(device_info.soc_pm_values, sizeof(device_info.soc_pm_values))){
std::cout << "PM Values: ";
extended_device_information_print_array(device_info.soc_pm_values, sizeof(device_info.soc_pm_values), "");
}
return HAILO_SUCCESS;
}
static std::string fw_version_string(const hailo_device_identity_t &identity)
{
std::stringstream os;
const auto fw_mode = ((identity.is_release) ? "release" : "develop");
// Currently will always return FW_BINARY_TYPE_APP_FIRMWARE as version bit is cleared in HailoRT
FW_BINARY_TYPE_t fw_binary_type = FIRMWARE_HEADER_UTILS__get_fw_binary_type(identity.fw_version.revision);
auto fw_type = "invalid";
if (FW_BINARY_TYPE_CORE_FIRMWARE == fw_binary_type) {
fw_type = "core";
} else if (FW_BINARY_TYPE_APP_FIRMWARE == fw_binary_type) {
fw_type = "app";
}
os << identity.fw_version.major << "." << identity.fw_version.minor << "."
<< identity.fw_version.revision << " (" << fw_mode << "," << fw_type << ")";
return os.str();
}
static std::string identity_arch_string(const hailo_device_identity_t &identity)
{
switch (identity.device_architecture) {
case HAILO_ARCH_HAILO8_B0:
return "HAILO8_B0";
case HAILO_ARCH_MERCURY_CA:
return "MERCURY_CA";
case HAILO_ARCH_MERCURY_VPU:
return "MERCURY_VPU";
default:
return "Unknown";
}
}
static std::string identity_attr_string(const char *attr, size_t attr_max_len)
{
size_t actual_len = strnlen(attr, attr_max_len);
if (actual_len == 0) {
return NOT_CONFIGURED_ATTR;
}
return std::string(attr, actual_len);
}
FwControlIdentifyCommand::FwControlIdentifyCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("identify", "Displays general information about the device")),
m_is_extended(false)
{
m_app->add_flag("--extended", m_is_extended, "Print device extended information");
}
hailo_status FwControlIdentifyCommand::execute_on_device(Device &device)
{
auto identity_expected = device.identify();
CHECK_EXPECTED_AS_STATUS(identity_expected, "Failed identify");
auto identity = identity_expected.release();
// Print board information
std::cout << "Identifying board" << std::endl;
std::cout << "Control Protocol Version: " << identity.protocol_version << std::endl;
std::cout << "Firmware Version: " << fw_version_string(identity) << std::endl;
std::cout << "Logger Version: " << identity.logger_version << std::endl;
std::cout << "Board Name: " << std::string(identity.board_name, identity.board_name_length) << std::endl;
std::cout << "Device Architecture: " << identity_arch_string(identity) << std::endl;
std::cout << "Serial Number: " <<
identity_attr_string(identity.serial_number, identity.serial_number_length) << std::endl;
std::cout << "Part Number: " <<
identity_attr_string(identity.part_number, identity.part_number_length) << std::endl;
std::cout << "Product Name: " <<
identity_attr_string(identity.product_name, identity.product_name_length) << std::endl;
if (m_is_extended) {
print_extended_device_information(device);
}
std::cout << std::endl;
return HAILO_SUCCESS;
}
FwControlResetCommand::FwControlResetCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("reset", "Resets the device"))
{
m_app->add_option("--reset-type", m_reset_mode, "Reset type")
->required()
->transform(HailoCheckedTransformer<hailo_reset_device_mode_t>({
{ "chip", HAILO_RESET_DEVICE_MODE_CHIP },
{ "nn_core", HAILO_RESET_DEVICE_MODE_NN_CORE },
{ "soft", HAILO_RESET_DEVICE_MODE_SOFT },
{ "forced_soft", HAILO_RESET_DEVICE_MODE_FORCED_SOFT },
}));
}
hailo_status FwControlResetCommand::execute_on_device(Device &device)
{
auto status = device.reset(m_reset_mode);
CHECK_SUCCESS(status, "Failed reset device");
std::cout << "Board has been reset successfully" << std::endl;
return HAILO_SUCCESS;
}
FwControlTestMemoriesCommand::FwControlTestMemoriesCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("test-memories", "Run a test of the chip's memories"))
{}
hailo_status FwControlTestMemoriesCommand::execute_on_device(Device &device)
{
auto status = device.test_chip_memories();
CHECK_SUCCESS(status, "Failed memory test");
std::cout << "Memory test has completed succesfully" << std::endl;
return HAILO_SUCCESS;
}
FwControlCommand::FwControlCommand(CLI::App &parent_app) :
ContainerCommand(parent_app.add_subcommand("fw-control", "Useful firmware control operations"))
{
add_subcommand<FwControlIdentifyCommand>();
add_subcommand<FwControlResetCommand>();
// TODO: Remove scan as a subcommand of fw_control_subcommand (HRT-2676)
// Can also remove Command::set_description function after this, and the return value of `add_subcommand`
auto &scan = add_subcommand<ScanSubcommand>();
scan.set_description("Alias for root-level 'scan' command (i.e. 'hailortcli scan...')\n"
"Note: 'scan' as a sub-command of 'fw-control' is deprecated; use 'hailortcli scan' instead\n"
" (or 'hailo scan' when using the 'hailo' command).");
add_subcommand<FwControlTestMemoriesCommand>();
// TODO: Support on windows (HRT-5919)
#if defined(__GNUC__)
// TODO: Unhide (HRT-6035)
static const bool HIDDEN = true;
add_subcommand<DownloadActionListCommand>(HIDDEN);
#endif
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_control.hpp
* @brief Several controls that can be sent to the firware
**/
#ifndef _HAILO_FW_CONTROL_COMMAND_HPP_
#define _HAILO_FW_CONTROL_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
// TODO: Remove scan as a subcommand of fw_control_subcommand (HRT-2676)
#include "scan_command.hpp"
#if defined(__GNUC__)
// TODO: Support on windows (HRT-5919)
#include "download_action_list_command.hpp"
#endif
class FwControlIdentifyCommand : public DeviceCommand {
public:
explicit FwControlIdentifyCommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
bool m_is_extended;
};
class FwControlResetCommand : public DeviceCommand {
public:
explicit FwControlResetCommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
hailo_reset_device_mode_t m_reset_mode;
};
class FwControlTestMemoriesCommand : public DeviceCommand {
public:
explicit FwControlTestMemoriesCommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
};
class FwControlCommand : public ContainerCommand {
public:
explicit FwControlCommand(CLI::App &parent_app);
};
#endif /* _HAILO_FW_CONTROL_COMMAND_HPP_ */

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_logger_command.cpp
* @brief Write fw log to output file
**/
#include "fw_logger_command.hpp"
#include "common/file_utils.hpp"
#define AMOUNT_OF_BYTES_TO_READ 256
FwLoggerCommand::FwLoggerCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("fw-logger", "Download fw logs to a file")),
m_should_overwrite(false)
{
m_app->add_option("output_file", m_output_file, "File path to write binary firmware log into")
->required();
m_app->add_flag("--overwrite", m_should_overwrite, "Should overwrite the file or not");
}
hailo_status write_logs_to_file(Device &device, std::ofstream &ofs, hailo_cpu_id_t cpu_id){
auto still_has_logs = true;
static const auto buffer_size = AMOUNT_OF_BYTES_TO_READ;
auto expected_buffer = Buffer::create(buffer_size);
CHECK_EXPECTED_AS_STATUS(expected_buffer);
Buffer buffer = expected_buffer.release();
while(still_has_logs) {
MemoryView response_view(buffer);
auto response_size_expected = device.read_log(response_view, cpu_id);
CHECK_EXPECTED_AS_STATUS(response_size_expected);
auto response_size = response_size_expected.release();
if (response_size == 0) {
still_has_logs = false;
}
else {
ofs.write((char *)buffer.data(), response_size);
CHECK(ofs.good(), HAILO_FILE_OPERATION_FAILURE,
"Failed writing firmware logger to output file, with errno: {}", errno);
}
}
return HAILO_SUCCESS;
}
hailo_status FwLoggerCommand::execute_on_device(Device &device)
{
auto ofs_flags = std::ios::out | std::ios::binary;
hailo_status status = HAILO_UNINITIALIZED;
if (!m_should_overwrite){
ofs_flags |= std::ios::app;
}
std::ofstream ofs(m_output_file, ofs_flags);
CHECK(ofs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening file: {}, with errno: {}", m_output_file, errno);
if (Device::Type::ETH == device.get_type()) {
LOGGER__ERROR("Read FW log is not supported over Eth device");
return HAILO_INVALID_OPERATION;
}
if (Device::Type::CORE != device.get_type()) {
status = write_logs_to_file(device, ofs, HAILO_CPU_ID_0);
if (status != HAILO_SUCCESS){
return status;
}
}
status = write_logs_to_file(device, ofs, HAILO_CPU_ID_1);
if (status != HAILO_SUCCESS){
return status;
}
return HAILO_SUCCESS;
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_logger_command.hpp
* @brief Write fw log to output file
**/
#ifndef _HAILO_FW_LOGGER_COMMAND_COMMAND_HPP_
#define _HAILO_FW_LOGGER_COMMAND_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "hailo/buffer.hpp"
#include "CLI/CLI.hpp"
class FwLoggerCommand : public DeviceCommand {
public:
explicit FwLoggerCommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_output_file;
bool m_should_overwrite;
};
#endif /* _HAILO_FW_LOGGER_COMMAND_COMMAND_HPP_ */

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_update_command.cpp
* @brief Update fw on hailo device with flash
**/
#include "fw_update_command.hpp"
#include "common/file_utils.hpp"
FwUpdateCommand::FwUpdateCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("fw-update", "Firmware update tool (only for flash based devices)")),
m_firmware_path(),
m_skip_reset(false)
{
m_app->add_option("firmware", m_firmware_path, "The path to the firmware binary")
->required()
->check(CLI::ExistingFile);
m_app->add_flag("--skip-reset", m_skip_reset, "Don't reset after update");
}
hailo_status FwUpdateCommand::execute_on_device(Device &device)
{
auto firmware = read_binary_file(m_firmware_path);
if (!firmware) {
std::cerr << "Failed reading firmware file " << firmware.status() << std::endl;
return firmware.status();
}
std::cout << "Updating firmware" << std::endl;
const bool should_reset = !m_skip_reset;
auto status = device.firmware_update(MemoryView(firmware->data(), static_cast<uint32_t>(firmware->size())), should_reset);
if (HAILO_SUCCESS != status) {
std::cerr << "Update firmware failed, error code " << status << std::endl;
return status;
}
std::cout << "Firmware has been updated" << std::endl;
return HAILO_SUCCESS;
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file fw_update_command.hpp
* @brief Update fw on hailo device with flash
**/
#ifndef _HAILO_FW_UPDATE_COMMAND_COMMAND_HPP_
#define _HAILO_FW_UPDATE_COMMAND_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "hailo/buffer.hpp"
#include "CLI/CLI.hpp"
class FwUpdateCommand : public DeviceCommand {
public:
explicit FwUpdateCommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_firmware_path;
bool m_skip_reset;
};
#endif /* _HAILO_FW_UPDATE_COMMAND_COMMAND_HPP_ */

View File

@@ -0,0 +1,8 @@
/* THIS FILE IS AUTOGENERATED! DO NOT MANUALLY EDIT. */
#ifndef _DEFINITIONS_JSON_AUTO_HPP_
#define _DEFINITIONS_JSON_AUTO_HPP_
static const char* g_fw_config_str_definitions = R"(${config_definitions_json_file})";
#endif /* _DEFINITIONS_JSON_AUTO_HPP_ */

View File

@@ -0,0 +1,393 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file graph_printer.cpp
* @brief Implementation of graph_printer module
**/
#include "graph_printer.hpp"
#include "hailo/hailort.h"
#include "common/filesystem.hpp"
#include "common/utils.hpp"
#include "infer_stats_printer.hpp"
#include <set>
#include <map>
#include <sstream>
#include <algorithm>
namespace hailort
{
PipelineGraphNode::PipelineGraphNode(DotWriter::RootGraph &G, std::shared_ptr<PipelineElement> elem,
const std::vector<AccumulatorPtr> &runtime_stats_accumulators,
DotWriter::Color::e node_color, DotWriter::Color::e node_bg_color,
const std::string &node_style, DotWriter::LabelLoc::e node_label_loc,
const std::string &sink_source_style, DotWriter::Color::e sink_source_color,
DotWriter::NodeShape::e sink_source_shape, double sink_source_height,
double sink_source_width, DotWriter::LabelLoc::e sub_label_loc) :
m_graph_node(nullptr),
m_sinks(),
m_sources(),
m_visited(false)
{
assert(nullptr != elem);
const auto label = create_multiline_label({ elem->name() /* TODO: other info will go here */ });
const auto sub_label = format_runtime_stats(runtime_stats_accumulators);
if (static_cast<std::string>(sub_label).empty()) {
m_graph_node = G.AddCluster(label);
assert(nullptr != m_graph_node);
} else {
auto *enclosing_cluster = G.AddCluster(sub_label);
assert(nullptr != enclosing_cluster);
enclosing_cluster->GetAttributes().SetLabelLoc(sub_label_loc);
enclosing_cluster->GetAttributes().SetPeripheries(0);
m_graph_node = enclosing_cluster->AddCluster(label);
assert(nullptr != m_graph_node);
m_graph_node->GetAttributes().SetTooltip(static_cast<std::string>(sub_label));
}
m_graph_node->GetAttributes().SetColor(node_color);
m_graph_node->GetAttributes().SetBGColor(node_bg_color);
m_graph_node->GetAttributes().SetStyle(node_style);
m_graph_node->GetAttributes().SetLabelLoc(node_label_loc);
m_graph_node->GetDefaultNodeAttributes().SetStyle(sink_source_style);
m_graph_node->GetDefaultNodeAttributes().SetColor(sink_source_color);
m_graph_node->GetDefaultNodeAttributes().SetShape(sink_source_shape);
m_graph_node->GetDefaultNodeAttributes().SetHeight(sink_source_height);
m_graph_node->GetDefaultNodeAttributes().SetWidth(sink_source_width);
m_graph_node->GetDefaultNodeAttributes().SetFixedSize(true);
for (const auto &sink : elem->sinks()) {
add_sink(sink);
}
if (0 == elem->sinks().size()) {
add_dummy_sink();
}
for (const auto &source : elem->sources()) {
add_source(source);
}
if (0 == elem->sources().size()) {
add_dummy_source();
}
}
DotWriter::HtmlString PipelineGraphNode::create_multiline_label(const std::vector<std::string> &lines, Align align_to)
{
if (lines.empty()) {
// Note: A html <table> without any "<tr></tr>" tags is invalid, so if lines is empty, we return an empty string
return DotWriter::HtmlString("");
}
std::stringstream result;
result << "<table border=\"0\">" << std::endl;
for (const auto& line : lines) {
result << "\t<tr><td align=\"text\">" << line;
if (align_to == Align::LEFT) {
result << "<br align=\"left\" />";
} else if (align_to == Align::RIGHT) {
result << "<br align=\"right\" />";
}
result << "</td></tr>" << std::endl;
}
result << "</table>";
return DotWriter::HtmlString(result.str());
}
DotWriter::HtmlString PipelineGraphNode::format_runtime_stats(const std::vector<AccumulatorPtr> &runtime_stats_accumulators)
{
std::vector<std::string> lines;
for (const auto &accumulator : runtime_stats_accumulators) {
if (nullptr == accumulator) {
continue;
}
const auto &accumulator_result = accumulator->get();
if ((!accumulator_result.count()) || (accumulator_result.count().value() == 0)) {
continue;
}
// We split the statistics into two lines
std::stringstream string_stream;
string_stream << "<B>" << accumulator->get_data_type() << ": </B>";
string_stream << "mean=" << InferResultsFormatUtils::format_statistic(accumulator_result.mean()) << ", ";
string_stream << "min=" << InferResultsFormatUtils::format_statistic(accumulator_result.min()) << ", ";
string_stream << "max=" << InferResultsFormatUtils::format_statistic(accumulator_result.max()) << ", ";
lines.emplace_back(string_stream.str());
// Clear the stream and format the next line
string_stream.str("");
string_stream << "var=" << InferResultsFormatUtils::format_statistic(accumulator_result.var()) << ", ";
string_stream << "sd=" << InferResultsFormatUtils::format_statistic(accumulator_result.sd()) << ", ";
string_stream << "mean_sd=" << InferResultsFormatUtils::format_statistic(accumulator_result.mean_sd());
lines.emplace_back(string_stream.str());
}
return create_multiline_label(lines, Align::LEFT);
}
void PipelineGraphNode::add_sink(const hailort::PipelinePad &pad)
{
auto *sink = m_graph_node->AddNode("sink");
assert(nullptr != sink);
sink->GetAttributes().SetTooltip(pad.name());
m_sinks.emplace(pad.name(), sink);
}
void PipelineGraphNode::add_dummy_sink()
{
auto *sink = m_graph_node->AddNode();
assert(nullptr != sink);
sink->GetAttributes().SetStyle("invis");
m_sinks.emplace("dummy_sink", sink);
}
void PipelineGraphNode::add_source(const hailort::PipelinePad &pad)
{
auto *source = m_graph_node->AddNode("source");
assert(nullptr != source);
source->GetAttributes().SetTooltip(pad.name());
m_sources.emplace(pad.name(), source);
if (!m_sinks.empty()) {
const auto first_sink_name = m_sinks.cbegin()->first;
auto *edge = m_graph_node->AddEdge(m_sinks[first_sink_name], source);
assert(nullptr != edge);
edge->GetAttributes().SetStyle("invis");
}
}
void PipelineGraphNode::add_dummy_source()
{
auto *source = m_graph_node->AddNode();
assert(nullptr != source);
source->GetAttributes().SetStyle("invis");
m_sources.emplace("dummy_source", source);
if (!m_sinks.empty()) {
const auto first_sink_name = m_sinks.cbegin()->first;
auto *edge = m_graph_node->AddEdge(m_sinks[first_sink_name], source);
assert(nullptr != edge);
edge->GetAttributes().SetStyle("invis");
}
}
DotWriter::Node *PipelineGraphNode::get_pad(const std::string &pad_name) const
{
if (m_sinks.count(pad_name) != 0) {
return m_sinks.at(pad_name);
}
assert(m_sources.count(pad_name) != 0);
return m_sources.at(pad_name);
}
bool PipelineGraphNode::has_been_visited() const
{
return m_visited;
}
void PipelineGraphNode::set_visited()
{
m_visited = true;
}
hailo_status GraphPrinter::write_dot_file(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network, const std::string &graph_title,
const std::string &output_path, bool write_pipeline_stats)
{
PipelineGraph graph(input_vstreams_per_network, output_vstreams_per_network, graph_title, write_pipeline_stats);
return graph.write_dot_file(output_path);
}
GraphPrinter::PipelineGraph::PipelineGraph(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network,
const std::string &graph_title, bool write_pipeline_stats) :
m_graph(true, create_graph_title_label(graph_title, DefaultNodeAttrs::MAIN_LABEL_FONT_SIZE)),
m_elems_in_graph()
{
size_t total_inputs_count = 0;
for (auto &input_vstreams_pair : input_vstreams_per_network) {
total_inputs_count += input_vstreams_pair.second.size();
}
size_t total_outputs_count = 0;
for (auto &output_vstreams_pair : output_vstreams_per_network) {
total_outputs_count += output_vstreams_pair.second.size();
}
// Set the graph "graph title" label to be on top
m_graph.GetAttributes().SetLabelLoc(DotWriter::LabelLoc::T);
// Set the graph direction from left to right
m_graph.GetAttributes().SetRankDir(DotWriter::RankDir::LR);
m_graph.GetAttributes().SetPackMode(format_pack_mode(total_outputs_count + total_inputs_count));
// Note: This order is important (input pipelines will be printed above output pipelines)
for (const auto &output_vstreams_pair : output_vstreams_per_network) {
for (const auto &vstream : output_vstreams_pair.second) {
update_graph_nodes(vstream.get_pipeline(), write_pipeline_stats);
}
}
for (const auto &input_vstreams_pair : input_vstreams_per_network) {
for (const auto &vstream : input_vstreams_pair.second) {
update_graph_nodes(vstream.get_pipeline(), write_pipeline_stats);
}
}
for (const auto &output_vstreams_pair : output_vstreams_per_network) {
for (const auto &vstream : output_vstreams_pair.second) {
update_edges_in_graph(vstream.get_pipeline(), "HW", "user_output");
}
}
for (const auto &input_vstreams_pair : input_vstreams_per_network) {
for (const auto &vstream : input_vstreams_pair.second) {
update_edges_in_graph(vstream.get_pipeline(), "user_input", "HW");
}
}
}
hailo_status GraphPrinter::PipelineGraph::write_dot_file(const std::string &output_path)
{
CHECK(m_graph.WriteToFile(output_path), HAILO_FILE_OPERATION_FAILURE,
"Faild writing graph '.dot' file to output_path='{}'", output_path);
return HAILO_SUCCESS;
}
DotWriter::Node *GraphPrinter::PipelineGraph::add_external_node(const std::string &label,
DotWriter::NodeShape::e shape, double height, double width)
{
auto *result = m_graph.AddNode(label);
assert(nullptr != result);
result->GetAttributes().SetShape(shape);
result->GetAttributes().SetHeight(height);
result->GetAttributes().SetWidth(width);
result->GetAttributes().SetFixedSize(true);
return result;
}
void GraphPrinter::PipelineGraph::update_graph_nodes(const std::vector<std::shared_ptr<PipelineElement>> &pipeline,
bool write_pipeline_stats)
{
// We assume that PipelineElement names are unique (also across different vstreams)
for (const auto& elem : pipeline) {
const auto elem_name = elem->name();
if (m_elems_in_graph.count(elem_name) != 0) {
// This elem appears in the graph
continue;
}
std::vector<AccumulatorPtr> runtime_stats_accumulators;
if (write_pipeline_stats) {
if (nullptr != elem->get_fps_accumulator()) {
runtime_stats_accumulators.emplace_back(elem->get_fps_accumulator());
}
if (nullptr != elem->get_latency_accumulator()) {
runtime_stats_accumulators.emplace_back(elem->get_latency_accumulator());
}
for (const auto &queue_size_accumulator : elem->get_queue_size_accumulators()) {
if (nullptr != queue_size_accumulator) {
runtime_stats_accumulators.emplace_back(queue_size_accumulator);
}
}
}
PipelineGraphNode curr_elem_graph_node(m_graph, elem, runtime_stats_accumulators);
m_elems_in_graph.emplace(elem_name, std::move(curr_elem_graph_node));
}
}
void GraphPrinter::PipelineGraph::update_edges_in_graph_recursive(const PipelineElement &root,
const std::string &output_elem_name)
{
auto &curr_elem_graph_node = m_elems_in_graph.at(root.name());
if (curr_elem_graph_node.has_been_visited()) {
return;
}
curr_elem_graph_node.set_visited();
for (const auto &source : root.sources()) {
if (nullptr == source.next()) {
auto *left = curr_elem_graph_node.get_pad(source.name());
auto *right = add_external_node(output_elem_name);
m_graph.AddEdge(left, right);
continue;
}
const auto *sink = source.next();
const auto &next_elem = sink->element();
const auto next_elem_name = next_elem.name();
auto *left = curr_elem_graph_node.get_pad(source.name());
auto *right = m_elems_in_graph.at(next_elem_name).get_pad(sink->name());
m_graph.AddEdge(left, right);
update_edges_in_graph_recursive(next_elem, output_elem_name);
}
}
void GraphPrinter::PipelineGraph::update_edges_in_graph(const std::vector<std::shared_ptr<PipelineElement>> &pipeline,
const std::string &input_elem_name, const std::string &output_elem_name)
{
for (const auto &root_elem : get_pipeline_root_elements(pipeline)) {
auto &root_elem_graph_node = m_elems_in_graph.at(root_elem->name());
if (root_elem_graph_node.has_been_visited()) {
continue;
}
for (const auto &sink : root_elem->sinks()) {
auto *left = add_external_node(input_elem_name);
auto *right = root_elem_graph_node.get_pad(sink.name());
m_graph.AddEdge(left, right);
}
update_edges_in_graph_recursive(*root_elem, output_elem_name);
}
}
std::string GraphPrinter::PipelineGraph::format_pack_mode(size_t num_rows)
{
// Align the graph as an array of num_rows rows (see https://graphviz.org/docs/attr-types/packMode/)
std::stringstream result;
result << "array_t" << std::to_string(num_rows);
return result.str();
}
DotWriter::HtmlString GraphPrinter::PipelineGraph::create_graph_title_label(const std::string &hef_path,
uint32_t font_size)
{
std::stringstream result;
result << "<font point-size=\"" << std::to_string(font_size) << "\">";
result << "<b>Network:</b> " << hef_path;
result << "</font>";
return DotWriter::HtmlString(result.str());
}
std::vector<std::shared_ptr<PipelineElement>> GraphPrinter::PipelineGraph::get_pipeline_root_elements(
const std::vector<std::shared_ptr<PipelineElement>> &pipeline)
{
std::vector<std::shared_ptr<PipelineElement>> root_elems;
for (auto& elem : pipeline) {
if ((0 == elem->sinks().size()) || std::all_of(elem->sinks().begin(), elem->sinks().end(),
[](auto &sink){ return nullptr == sink.prev(); })) {
// A PipelineElement is a "root" if it has no sink pads or it's sink pads aren't connected to source pads
root_elems.emplace_back(elem);
}
}
return root_elems;
}
} /* namespace hailort */

View File

@@ -0,0 +1,163 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file graph_printer.hpp
* @brief Print pipeline graphs to '.dot' files
**/
#ifndef _HAILO_GRAPH_PRINTER_HPP_
#define _HAILO_GRAPH_PRINTER_HPP_
#include "hailo/hailort.h"
#include "hailo/vstream.hpp"
#include "pipeline.hpp"
#include "DotWriter.h"
#include <vector>
#include <memory>
#include <type_traits>
namespace hailort
{
class DefaultNodeAttrs final
{
public:
DefaultNodeAttrs() = delete;
// Titles
static constexpr uint32_t MAIN_LABEL_FONT_SIZE = 28;
static constexpr uint32_t PIPELINE_LABEL_FONT_SIZE = 24;
// Pipeline nodes
static constexpr DotWriter::Color::e PIPELINE_NODE_COLOR = DotWriter::Color::LIGHTGREY;
static constexpr DotWriter::Color::e PIPELINE_NODE_BG_COLOR = DotWriter::Color::LIGHTGREY;
static std::string PIPELINE_NODE_STYLE() { return "rounded"; };
static constexpr DotWriter::LabelLoc::e PIPELINE_NODE_LABEL_LOC = DotWriter::LabelLoc::T;
// Sink/source nodes
static std::string SINK_SOURCE_STYLE() { return "filled"; };
static constexpr DotWriter::Color::e SINK_SOURCE_COLOR = DotWriter::Color::WHITE;
static constexpr DotWriter::NodeShape::e SINK_SOURCE_SHAPE = DotWriter::NodeShape::RECTANGLE;
static constexpr double SINK_SOURCE_HEIGHT = 0.5;
static constexpr double SINK_SOURCE_WIDTH = 0.7;
// External nodes (HW, user_input, user_output)
static constexpr DotWriter::NodeShape::e EXTERNAL_NODE_SHAPE = DotWriter::NodeShape::RECTANGLE;
static constexpr double EXTERNAL_NODE_HEIGHT = 0.5;
static constexpr double EXTERNAL_NODE_WIDTH = 1;
// Sublabel
static constexpr DotWriter::LabelLoc::e SUBLABEL_LOC = DotWriter::LabelLoc::B;
};
class PipelineGraphNode final
{
public:
PipelineGraphNode(DotWriter::RootGraph &G, std::shared_ptr<PipelineElement> elem,
const std::vector<AccumulatorPtr> &runtime_stats_accumulators,
DotWriter::Color::e node_color = DefaultNodeAttrs::PIPELINE_NODE_COLOR,
DotWriter::Color::e node_bg_color = DefaultNodeAttrs::PIPELINE_NODE_BG_COLOR,
const std::string &node_style = DefaultNodeAttrs::PIPELINE_NODE_STYLE(),
DotWriter::LabelLoc::e node_label_loc = DefaultNodeAttrs::PIPELINE_NODE_LABEL_LOC,
const std::string &sink_source_style = DefaultNodeAttrs::SINK_SOURCE_STYLE(),
DotWriter::Color::e sink_source_color = DefaultNodeAttrs::SINK_SOURCE_COLOR,
DotWriter::NodeShape::e sink_source_shape = DefaultNodeAttrs::SINK_SOURCE_SHAPE,
double sink_source_height = DefaultNodeAttrs::SINK_SOURCE_HEIGHT,
double sink_source_width = DefaultNodeAttrs::SINK_SOURCE_WIDTH,
DotWriter::LabelLoc::e sub_label_loc = DefaultNodeAttrs::SUBLABEL_LOC);
PipelineGraphNode(PipelineGraphNode &&) = default;
PipelineGraphNode(const PipelineGraphNode &) = delete;
PipelineGraphNode &operator=(PipelineGraphNode &&) = delete;
PipelineGraphNode &operator=(const PipelineGraphNode &) = delete;
~PipelineGraphNode() = default;
DotWriter::Node *get_pad(const std::string &pad_name) const;
bool has_been_visited() const;
void set_visited();
private:
enum class Align
{
LEFT,
RIGHT,
CENTER
};
// Creates a HtmlString with each line in lines on a new line, or an empty string if lines is empty
static DotWriter::HtmlString create_multiline_label(const std::vector<std::string> &lines, Align align_to = Align::CENTER);
static DotWriter::HtmlString format_runtime_stats(const std::vector<AccumulatorPtr> &runtime_stats_accumulators);
// Note:
// * Sink/source pads are nested under the pipeline element node.
// * Sinks always appear on the left side of the pipeline element node, and sources on the right.
// * If an element has no sinks/soruces, then an invisible "dummy" sink/source is added.
// This is used to align the other pads in the elem to the left/right (a Graphviz oddity).
// E.g:
// ------------------------------------ ------------------------------------
// | Filter_Elem1 | | Sink_Elem1 |
// | | | |
// | -------- -------- | | -------- |
// --|-->| sink | |source|----|---------------|-->| sink | (dummy source) |
// | -------- -------- | | -------- (invisible) |
// | | | |
// ------------------------------------ ------------------------------------
void add_sink(const hailort::PipelinePad &pad);
void add_dummy_sink();
void add_source(const hailort::PipelinePad &pad);
void add_dummy_source();
DotWriter::Cluster *m_graph_node;
std::map<std::string, DotWriter::Node *> m_sinks;
std::map<std::string, DotWriter::Node *> m_sources;
bool m_visited;
};
class GraphPrinter
{
public:
GraphPrinter() = delete;
static hailo_status write_dot_file(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network,
const std::string &graph_title, const std::string &output_path, bool write_pipeline_stats);
private:
class PipelineGraph final
{
public:
PipelineGraph(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network,
const std::string &graph_title, bool write_pipeline_stats);
hailo_status write_dot_file(const std::string &output_path);
private:
DotWriter::RootGraph m_graph;
std::map<std::string, PipelineGraphNode> m_elems_in_graph;
DotWriter::Node *add_external_node(const std::string &label,
DotWriter::NodeShape::e shape = DefaultNodeAttrs::EXTERNAL_NODE_SHAPE,
double height = DefaultNodeAttrs::EXTERNAL_NODE_HEIGHT,
double width = DefaultNodeAttrs::EXTERNAL_NODE_WIDTH);
// Add all the pipeline nodes in the graph to m_graph
void update_graph_nodes(const std::vector<std::shared_ptr<PipelineElement>> &pipeline, bool write_pipeline_stats);
// update_edges_* will be called after adding the graph nodes with 'update_graph_nodes'
// Update the edges in the pipeline graph recursively, starting at root (using DFS)
void update_edges_in_graph_recursive(const PipelineElement &root, const std::string &output_elem_name);
// Update all of the edges in the pipeline graph
void update_edges_in_graph(const std::vector<std::shared_ptr<PipelineElement>> &pipeline,
const std::string &input_elem_name, const std::string &output_elem_name);
static std::string format_pack_mode(size_t num_rows);
static DotWriter::HtmlString create_graph_title_label(const std::string &hef_path, uint32_t font_size);
static std::vector<std::shared_ptr<PipelineElement>> get_pipeline_root_elements(
const std::vector<std::shared_ptr<PipelineElement>> &pipeline);
};
};
} /* namespace hailort */
#endif /* _HAILO_GRAPH_PRINTER_HPP_ */

View File

@@ -0,0 +1,246 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file hailortcli.cpp
* @brief HailoRT CLI.
*
* HailoRT command line interface.
**/
#include "hailortcli.hpp"
#include "scan_command.hpp"
#include "power_measurement_command.hpp"
#include "run_command.hpp"
#include "fw_update_command.hpp"
#include "ssb_update_command.hpp"
#include "sensor_config_command.hpp"
#include "board_config_command.hpp"
#include "fw_config_command.hpp"
#include "fw_logger_command.hpp"
#include "benchmark_command.hpp"
#if defined(__GNUC__)
#include "udp_rate_limiter_command.hpp"
#endif
#include "parse_hef_command.hpp"
#include "fw_control_command.hpp"
#include "firmware_header_utils.h"
#include "hailo/hailort.h"
#include "hailo/hailort_common.hpp"
#include "hailo/device.hpp"
#include "hailo/hef.hpp"
#include "hailo/buffer.hpp"
#include "CLI/CLI.hpp"
#include <spdlog/sinks/stdout_color_sinks.h>
#include <memory>
#include <iostream>
#include <vector>
#include <thread>
#include <map>
static Expected<hailo_pcie_device_info_t> get_pcie_device_info(const hailo_pcie_params &pcie_params)
{
if (pcie_params.pcie_bdf.empty()) {
auto scan_result = Device::scan_pcie();
if (!scan_result) {
std::cerr << "Hailo PCIe scan failed (maybe pcie device not exists). status=" << scan_result.status() << std::endl;
return make_unexpected(scan_result.status());
}
if (scan_result->size() == 0) {
std::cerr << "Hailo PCIe not found.." << std::endl;
return make_unexpected(HAILO_INTERNAL_FAILURE);
}
return std::move(scan_result->at(0));
} else {
auto device_info_expected = Device::parse_pcie_device_info(pcie_params.pcie_bdf);
if (!device_info_expected) {
std::cerr << "Invalid pcie bdf format" << std::endl;
return make_unexpected(device_info_expected.status());
}
return device_info_expected.release();
}
}
Expected<std::unique_ptr<Device>> create_pcie_device(const hailo_pcie_params &pcie_params)
{
auto device_info = get_pcie_device_info(pcie_params);
if (!device_info) {
return make_unexpected(device_info.status());
}
auto device = Device::create_pcie(device_info.value());
if (!device) {
std::cerr << "Failed create pcie device. status=" << device.status() << std::endl;
return make_unexpected(device.status());
}
return Expected<std::unique_ptr<Device>>(device.release());
}
static Expected<std::unique_ptr<Device>> create_eth_device(const hailo_eth_params &eth_params)
{
auto device = Device::create_eth(eth_params.ip_addr);
if (!device) {
std::cerr << "Failed create ethernet device. status=" << device.status() << std::endl;
return make_unexpected(device.status());
}
return Expected<std::unique_ptr<Device>>(device.release());
}
Expected<std::unique_ptr<Device>> create_device(const hailo_device_params &device_params)
{
switch (device_params.device_type) {
case DeviceType::PCIE:
return create_pcie_device(device_params.pcie_params);
case DeviceType::ETH:
return create_eth_device(device_params.eth_params);
case DeviceType::DEFAULT:
// If core driver is loaded (we are running on Mercury) then the default is core device; else, pcie device
if (Device::is_core_driver_loaded()) {
return Device::create_core_device();
} else {
return create_pcie_device(device_params.pcie_params);
}
default:
std::cerr << "Invalid device type" << std::endl;
return make_unexpected(HAILO_INVALID_ARGUMENT);
}
}
void add_device_options(CLI::App *app, hailo_device_params &device_params)
{
// Initialize the device type to default
device_params.device_type = DeviceType::DEFAULT;
auto group = app->add_option_group("Device Options");
// TODO: `--target` and `udp` DeviceType::ETH are for backwards compatibility with the python implemention (`hailo`)
// TODO: Remove them (HRT-2676)
const HailoCheckedTransformer<DeviceType> device_type_transformer({
{ "pcie", DeviceType::PCIE },
{ "eth", DeviceType::ETH },
{ "udp", DeviceType::ETH },
});
auto *device_type_option = group->add_option("-d,--device-type,--target", device_params.device_type,
"Device type to use\n"
"Default is pcie.\n"
"Note: 'udp' is an alias for 'eth'.")
->transform(device_type_transformer);
std::vector<DeprecationActionPtr> actions{ std::make_shared<ValueDeprecation>(device_type_option, "udp", "eth") };
hailo_deprecate_options(app, actions, false);
// PCIe options
auto *pcie_bdf_option = group->add_option("-s,--bdf", device_params.pcie_params.pcie_bdf,
"Device id ([<domain>]:<bus>:<device>.<func>, same as in lspci command)")
->default_val("");
// Ethernet options
auto *ip_option = group->add_option("--ip", device_params.eth_params.ip_addr, "IP address of the target")
->default_val("")
->check(CLI::ValidIPV4);
group->parse_complete_callback([&device_params, device_type_option, pcie_bdf_option, ip_option](){
// The user didn't put target, we can figure it ourself
if (device_type_option->empty()) {
if (!ip_option->empty()) {
// User gave IP, target is eth
device_params.device_type = DeviceType::ETH;
} else if (!pcie_bdf_option->empty()) {
// User gave bdf, target is pcie
device_params.device_type = DeviceType::PCIE;
}
else {
device_params.device_type = DeviceType::DEFAULT;
}
}
if (ip_option->empty() && device_params.device_type == DeviceType::ETH) {
throw CLI::ParseError("IP address is not set", CLI::ExitCodes::InvalidError);
}
if (!ip_option->empty() && device_params.device_type != DeviceType::ETH) {
throw CLI::ParseError("IP address is set on non eth device", CLI::ExitCodes::InvalidError);
}
if (!pcie_bdf_option->empty() && device_params.device_type != DeviceType::PCIE) {
throw CLI::ParseError("bdf (-s) is set on non pcie device", CLI::ExitCodes::InvalidError);
}
});
}
void add_vdevice_options(CLI::App *app, hailo_device_params &device_params) {
auto group = app->add_option_group("VDevice Options");
// VDevice options
auto *device_count_option = group->add_option("--device-count", device_params.vdevice_params.device_count, "VDevice device count")
->default_val(HAILO_DEFAULT_DEVICE_COUNT)
->check(CLI::PositiveNumber);
group->parse_complete_callback([&device_params, device_count_option](){
// The user gave device_count
if (!device_count_option->empty()) {
if ((device_params.vdevice_params.device_count > 1) &&
((DeviceType::ETH == device_params.device_type) || (DeviceType::PCIE == device_params.device_type && !device_params.pcie_params.pcie_bdf.empty()))) {
throw CLI::ParseError("Device type must not be specified when using multiple devices", CLI::ExitCodes::InvalidError);
}
}
});
}
class HailoRTCLI : public ContainerCommand {
public:
HailoRTCLI(CLI::App *app) : ContainerCommand(app)
{
m_app->add_flag_callback("-v,--version",
[] () {
std::cout << "hailortcli version " <<
HAILORT_MAJOR_VERSION << "." << HAILORT_MINOR_VERSION << "." << HAILORT_REVISION_VERSION << std::endl;
// throw CLI::Success to stop parsing and not failing require_subcommand(1) we set earlier
throw (CLI::Success{});
},
"Print program version and exit"
);
add_subcommand<RunCommand>();
add_subcommand<ScanSubcommand>();
add_subcommand<BenchmarkCommand>();
add_subcommand<PowerMeasurementSubcommand>();
add_subcommand<SensorConfigCommand>();
add_subcommand<BoardConfigCommand>();
add_subcommand<FwConfigCommand>();
add_subcommand<FwLoggerCommand>();
add_subcommand<FwUpdateCommand>();
add_subcommand<SSBUpdateCommand>();
#if defined(__GNUC__)
add_subcommand<UdpRateLimiterCommand>();
#endif
add_subcommand<ParseHefCommand>();
add_subcommand<FwControlCommand>();
}
int parse_and_execute(int argc, char **argv)
{
CLI11_PARSE(*m_app, argc, argv);
return execute();
}
};
int main(int argc, char** argv) {
auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
console_sink->set_level(spdlog::level::info);
console_sink->set_pattern("[%n] [%^%l%$] %v");
spdlog::set_default_logger(std::make_shared<spdlog::logger>("HailoRT CLI", console_sink));
CLI::App app{"HailoRT CLI"};
HailoRTCLI cli(&app);
return cli.parse_and_execute(argc, argv);
}

View File

@@ -0,0 +1,202 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file hailortcli.hpp
* @brief HailoRT CLI.
**/
#ifndef _HAILO_HAILORTCLI_HPP_
#define _HAILO_HAILORTCLI_HPP_
#include "hailo/hailort.h"
#include "hailo/expected.hpp"
#include "hailo/device.hpp"
#include "common/logger_macros.hpp"
#include "common/utils.hpp"
#include "CLI/CLI.hpp"
#include <string>
using namespace hailort;
#define PARSE_CHECK(cond, message) \
do { \
if (!(cond)) { \
throw CLI::ParseError(message, CLI::ExitCodes::InvalidError); \
} \
} while (0)
struct hailo_pcie_params {
std::string pcie_bdf; // if empty use the first scanned
};
struct hailo_eth_params {
std::string ip_addr;
};
struct hailo_vdevice_params {
uint32_t device_count;
};
enum class DeviceType {
PCIE = 0,
ETH,
DEFAULT
};
struct hailo_device_params {
DeviceType device_type;
hailo_pcie_params pcie_params;
hailo_eth_params eth_params;
hailo_vdevice_params vdevice_params;
};
void add_device_options(CLI::App *app, hailo_device_params &device_params);
void add_vdevice_options(CLI::App *app, hailo_device_params &device_params);
Expected<std::unique_ptr<Device>> create_device(const hailo_device_params &device_params);
Expected<std::unique_ptr<Device>> create_pcie_device(const hailo_pcie_params &pcie_params);
/**
* CLI11 transformer object, converting enum argument from string.
* Use this object instead of CLI::CheckedTransformer in order
* to avoid ugly prints in the help message.
*/
template<typename EnumType>
class HailoCheckedTransformer : public CLI::CheckedTransformer
{
public:
HailoCheckedTransformer(std::vector<std::pair<std::string, EnumType>> values) :
CLI::CheckedTransformer(values)
{
desc_function_ = [values]() {
return CLI::detail::generate_map(CLI::detail::smart_deref(values), true);
};
}
};
class DeprecationAction
{
public:
DeprecationAction() = default;
virtual ~DeprecationAction() = default;
virtual std::string deprecate(bool message_inline) = 0;
static std::string get_inline_description(CLI::Option *opt, const std::string &message)
{
const auto orig_desc = opt->get_description();
std::stringstream new_desc;
if (!orig_desc.empty()) {
new_desc << orig_desc;
if (orig_desc.back() != '\n') {
new_desc << std::endl;
}
}
new_desc << "Note: " << message;
return new_desc.str();
}
};
using DeprecationActionPtr = std::shared_ptr<DeprecationAction>;
class OptionDeprecation : public DeprecationAction
{
public:
OptionDeprecation(CLI::Option *opt, const std::string &replacement) :
DeprecationAction(),
m_opt(opt),
m_replacement(replacement)
{
assert(nullptr != opt);
}
virtual ~OptionDeprecation() = default;
// Based off of CLI::deprecate_option, changed logic to suit our needs
virtual std::string deprecate(bool message_inline) override
{
std::stringstream message;
message << "'" << m_opt->get_name() << "' is deprecated, please use '" << m_replacement << "' instead." << std::endl;
CLI::Validator deprecate_warning(
[message = message.str()](std::string &) {
std::cout << message;
return std::string();
}, message_inline ? "" : "DEPRECATED");
deprecate_warning.application_index(0);
if (message_inline) {
m_opt->description(get_inline_description(m_opt, message.str()));
}
m_opt->check(deprecate_warning);
return message.str();
}
private:
CLI::Option *const m_opt;
const std::string m_replacement;
};
class ValueDeprecation : public DeprecationAction
{
public:
ValueDeprecation(CLI::Option *opt, const std::string &value, const std::string &replacement) :
DeprecationAction(),
m_opt(opt),
m_value(value),
m_replacement(replacement)
{
assert(nullptr != opt);
}
virtual ~ValueDeprecation() = default;
// Based off of CLI::deprecate_option, changed logic to suit our needs
virtual std::string deprecate(bool message_inline) override
{
std::stringstream message;
message << "'" << m_value << "' is deprecated, please use '" << m_replacement << "' instead." << std::endl;
// We capture the members by value (i.e. copy), since the Validator can outlive this object
CLI::Validator deprecate_warning(
[message = message.str(), opt = m_opt, value = m_value](std::string &) {
const auto results = opt->results();
if ((results.size() == 1) && (results[0] == value)) {
std::cout << message;
}
return std::string();
}, "");
deprecate_warning.application_index(0);
if (message_inline) {
m_opt->description(get_inline_description(m_opt, message.str()));
}
// Hack: transform() and not check(), because we want the string values of opt->results() and not the enum values after HailoCheckedTransformer
// transform places the Validator at the head of the validators in opt, so we'll get this check before the transformation is done
m_opt->transform(deprecate_warning);
return message.str();
}
private:
CLI::Option *const m_opt;
const std::string m_value;
const std::string m_replacement;
};
inline void hailo_deprecate_options(CLI::App *app, const std::vector<DeprecationActionPtr> &actions, bool set_footer = true)
{
// std::set and not std::vector in case two actions have the smae deprection string
std::set<std::string> deprecation_messages;
for (const auto& deprecation_action : actions) {
deprecation_messages.insert(deprecation_action->deprecate(!set_footer));
}
if (set_footer) {
std::stringstream footer_message;
footer_message << "Deprecated flags/options:" << std::endl;
for (const auto &message : deprecation_messages) {
footer_message << " * " << message;
if (message.back() != '\n') {
footer_message << std::endl;
}
}
app->footer(footer_message.str());
}
}
#endif /* _HAILO_HAILORTCLI_HPP_ */

View File

@@ -0,0 +1,459 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file infer_stats_printer.cpp
* @brief Show inference progress
**/
#include "infer_stats_printer.hpp"
#include "run_command.hpp"
#include "common.hpp"
#include <fstream>
#include <iostream>
#include <sstream>
static std::string infer_mode_to_string(InferMode infer_mode)
{
switch (infer_mode) {
case InferMode::STREAMING:
return "streaming";
case InferMode::HW_ONLY:
return "hw_only";
default:
return "???";
}
}
std::string InferResultsFormatUtils::format_statistic(const Expected<double> &statistic, uint32_t precision)
{
if (!statistic.has_value()) {
return "-";
}
std::stringstream string_stream;
string_stream << std::fixed << std::setprecision(precision) << statistic.value();
return string_stream.str();
}
std::string InferResultsFormatUtils::format_statistic(const Expected<size_t> &statistic)
{
if (!statistic.has_value()) {
return "-";
}
return std::to_string(statistic.value());
}
double InferResultsFormatUtils::latency_result_to_ms(std::chrono::nanoseconds latency)
{
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(latency).count();
}
Expected<InferStatsPrinter> InferStatsPrinter::create(const inference_runner_params &params, bool print_running_info)
{
hailo_status status = HAILO_UNINITIALIZED;
InferStatsPrinter printer(params, status, print_running_info);
CHECK_SUCCESS_AS_EXPECTED(status);
return printer;
}
InferStatsPrinter::InferStatsPrinter(const inference_runner_params &params, hailo_status &output_status, bool print_running_info) :
m_print_frame_count(0 != params.time_to_run)
{
if (!params.csv_output.empty()) {
m_results_csv_path = params.csv_output;
m_results_csv_file.open(params.csv_output.c_str(), std::ios::out);
if (!m_results_csv_file.good()) {
LOGGER__ERROR("Failed creating csv output file {}", params.csv_output);
output_status = HAILO_OPEN_FILE_FAILURE;
return;
}
}
if (should_measure_pipeline_stats(params)) {
m_pipeline_stats_csv_path = params.pipeline_stats.pipeline_stats_output_path;
m_pipeline_stats_csv_file.open(params.pipeline_stats.pipeline_stats_output_path.c_str(),
std::ios::out);
if (!m_pipeline_stats_csv_file.good()) {
LOGGER__ERROR("Failed creating pipeline stats csv output file {}",
params.pipeline_stats.pipeline_stats_output_path);
output_status = HAILO_OPEN_FILE_FAILURE;
return;
}
}
if (print_running_info) {
std::cout << "Running " << infer_mode_to_string(params.mode) << " inference (" << params.hef_path << "):" << std::endl;
std::cout << " Transform data: " << std::boolalpha << params.transform.transform << std::endl;
if (params.transform.transform) {
std::cout << " Type: " << format_type_to_string(params.transform.format_type) << std::endl;
std::cout << " Quantized: " << std::boolalpha << params.transform.quantized << std::endl;
}
}
if (!params.dot_output.empty()) {
m_dot_output_path = params.dot_output;
}
output_status = HAILO_SUCCESS;
}
void InferStatsPrinter::print(const std::string &network_group_name, Expected<NetworkGroupInferResult>& inference_result)
{
if (m_results_csv_file.is_open()) {
std::cout << "> Writing inference results to '" << m_results_csv_path << "'... ";
print_csv(network_group_name, inference_result);
std::cout << "done." << std::endl;
}
if (m_pipeline_stats_csv_file.is_open()) {
std::cout << "> Writing pipeline statistics to '" << m_pipeline_stats_csv_path << "'... ";
m_pipeline_stats_csv_file << "net_name,vstream_name,param_type,element,mean,min,max,var,sd,mean_sd,index" << std::endl;
print_pipeline_elem_stats_csv(network_group_name, inference_result->m_fps_accumulators);
print_pipeline_elem_stats_csv(network_group_name, inference_result->m_latency_accumulators);
print_pipeline_elem_stats_csv(network_group_name, inference_result->m_queue_size_accumulators);
print_entire_pipeline_stats_csv(network_group_name, inference_result->m_pipeline_latency_accumulators);
std::cout << "done." << std::endl;
}
print_stdout(inference_result);
}
void InferStatsPrinter::print_csv_header()
{
m_results_csv_file << "net_name,status,status_description,fps,num_of_frames,send_rate,recv_rate,hw_latency,overall_latency,min_power,average_power,max_power,min_current,average_current,max_current,min_temp,average_temp,max_temp" << std::endl;
}
void InferStatsPrinter::print_benchmark_csv_header()
{
m_results_csv_file << "net_name,fps,hw_only_fps,num_of_frames,num_of_frames_hw_only,hw_latency,overall_latency,min_power,average_power,max_power" << std::endl;
}
void InferStatsPrinter::print_csv(const std::string &network_group_name, Expected<NetworkGroupInferResult>& inference_result)
{
auto status_description = hailo_get_status_message(inference_result.status());
m_results_csv_file << network_group_name << "," << static_cast<uint32_t>(inference_result.status()) << "," << status_description;
if (!inference_result) {
m_results_csv_file << ",,,,,,,,,,,";
}
else {
m_results_csv_file << ",";
if (auto fps = inference_result->fps()) {
m_results_csv_file << fps.value();
}
m_results_csv_file << ",";
if (auto frames_count = inference_result->frames_count()) {
m_results_csv_file << frames_count.value();
}
m_results_csv_file << ",";
if (auto send_data_rate = inference_result->send_data_rate_mbit_sec()) {
m_results_csv_file << send_data_rate.value();
}
m_results_csv_file << ",";
if (auto recv_data_rate = inference_result->recv_data_rate_mbit_sec()) {
m_results_csv_file << recv_data_rate.value();
}
m_results_csv_file << ",";
if (auto hw_latency = inference_result->hw_latency()) {
m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value());
}
m_results_csv_file << ",";
if (auto overall_latency = inference_result->overall_latency()) {
m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value());
}
// TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
if (1 == inference_result->m_power_measurements.size()) {
for (const auto &pair : inference_result->m_power_measurements) {
if (nullptr != pair.second) {
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().min_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().average_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().max_value;
} else {
m_results_csv_file << ",,,";
}
}
} else {
m_results_csv_file << ",,,";
}
// TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
if (1 == inference_result->m_current_measurements.size()) {
for (const auto &pair : inference_result->m_current_measurements) {
if (nullptr != pair.second) {
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().min_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().average_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().max_value;
} else {
m_results_csv_file << ",,,";
}
}
} else {
m_results_csv_file << ",,,";
}
// TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
if (1 == inference_result->m_temp_measurements.size()) {
for (const auto &pair : inference_result->m_temp_measurements) {
if (nullptr != pair.second) {
m_results_csv_file << ",";
m_results_csv_file << pair.second->min_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->average_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->max_value;
} else {
m_results_csv_file << ",,,";
}
}
} else {
m_results_csv_file << ",,,";
}
}
m_results_csv_file << std::endl;
}
void InferStatsPrinter::print_pipeline_elem_stats_csv(const std::string &network_group_name,
const std::map<std::string, std::map<std::string, AccumulatorPtr>> &inference_result)
{
if (inference_result.size() == 0) {
return;
}
for (const auto &vstream_name_results_pair : inference_result) {
for (const auto &elem_name_accumulator_pair : vstream_name_results_pair.second) {
write_accumulator_results(m_pipeline_stats_csv_file, elem_name_accumulator_pair.second,
network_group_name, vstream_name_results_pair.first, elem_name_accumulator_pair.first);
}
}
}
void InferStatsPrinter::print_pipeline_elem_stats_csv(const std::string &network_group_name,
const std::map<std::string, std::map<std::string, std::vector<AccumulatorPtr>>> &inference_result)
{
if (inference_result.size() == 0) {
return;
}
for (const auto &vstream_name_results_pair : inference_result) {
for (const auto &elem_name_accumulator_pair : vstream_name_results_pair.second) {
for (uint32_t i = 0; i < elem_name_accumulator_pair.second.size(); i++) {
write_accumulator_results(m_pipeline_stats_csv_file, elem_name_accumulator_pair.second[i],
network_group_name, vstream_name_results_pair.first, elem_name_accumulator_pair.first, i);
}
}
}
}
void InferStatsPrinter::print_entire_pipeline_stats_csv(const std::string &network_group_name,
const std::map<std::string, AccumulatorPtr> &inference_result)
{
if (inference_result.size() == 0) {
return;
}
for (const auto &vstream_name_results_pair : inference_result) {
write_accumulator_results(m_pipeline_stats_csv_file, vstream_name_results_pair.second,
network_group_name, vstream_name_results_pair.first, "entire_pipeline");
}
}
void InferStatsPrinter::print_benchmark_csv(const std::string &network_group_name, const NetworkGroupInferResult &hw_inference_result,
const NetworkGroupInferResult &streaming_inference_result, const NetworkGroupInferResult &hw_latency_result)
{
m_results_csv_file << network_group_name << ",";
if (auto fps = streaming_inference_result.fps()) {
m_results_csv_file << fps.value();
}
m_results_csv_file << ",";
if (auto hw_only_fps = hw_inference_result.fps()) {
m_results_csv_file << hw_only_fps.value();
}
m_results_csv_file << ",";
if (auto frames_count = streaming_inference_result.frames_count()) {
m_results_csv_file << frames_count.value();
}
m_results_csv_file << ",";
if (auto frames_count = hw_inference_result.frames_count()) {
m_results_csv_file << frames_count.value();
}
m_results_csv_file << ",";
if (auto hw_latency = hw_latency_result.hw_latency()) {
m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value());
}
m_results_csv_file << ",";
if (auto overall_latency = hw_latency_result.overall_latency()) {
m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value());
}
// TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
if (1 == streaming_inference_result.m_power_measurements.size()) {
for (const auto &pair : streaming_inference_result.m_power_measurements) {
if (nullptr != pair.second) {
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().min_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().average_value;
m_results_csv_file << ",";
m_results_csv_file << pair.second->data().max_value;
} else {
m_results_csv_file << ",,,";
}
}
} else {
m_results_csv_file << ",,,";
}
m_results_csv_file << std::endl;
}
template< typename T>
void InferStatsPrinter::print_stdout_single_element(const T &results, size_t frames_count)
{
if (0 != frames_count) {
std::cout << " Frames count: " << static_cast<uint32_t>(frames_count) << std::endl;
} else if (auto duration = results.infer_duration()) {
std::cout << " Duration: " << CliCommon::duration_to_string(std::chrono::seconds(static_cast<uint32_t>(*duration))) << std::endl;
}
if (auto fps = results.fps()) {
std::cout << " FPS: " << fps.value() << "" << std::endl;
}
if (auto send_data_rate = results.send_data_rate_mbit_sec()) {
std::cout << " Send Rate: " << send_data_rate.value() << " Mbit/s" << std::endl;
}
if (auto recv_data_rate = results.recv_data_rate_mbit_sec()) {
std::cout << " Recv Rate: " << recv_data_rate.value() << " Mbit/s" << std::endl;
}
if (auto hw_latency = results.hw_latency()) {
std::cout << " HW Latency: " << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value()) << " ms" << std::endl;
}
if (auto overall_latency = results.overall_latency()) {
std::cout << " Overall Latency: " << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value()) << " ms" << std::endl;
}
}
void InferStatsPrinter::print_stdout(Expected<NetworkGroupInferResult>& inference_result)
{
if (!inference_result) {
return;
}
// Set precision and flags
auto original_precision = std::cout.precision();
auto original_flags(std::cout.flags());
std::cout << std::setprecision(2) << std::fixed;
std::cout << FORMAT_CLEAR_LINE << "> Inference result:" << std::endl;
if (1 < inference_result->m_result_per_network.size()) {
// If there is more than 1 network, we print results per network, and than sum of bandwith
for (auto &network_result_pair : inference_result->m_result_per_network) {
std::cout << " Network: " << network_result_pair.first << std::endl;
auto frames_count = (m_print_frame_count) ? network_result_pair.second.m_frames_count : 0;
print_stdout_single_element<NetworkInferResult>(network_result_pair.second, frames_count);
}
std::stringstream bandwidth_stream;
bandwidth_stream << std::setprecision(2) << std::fixed;
if (auto send_data_rate = inference_result->send_data_rate_mbit_sec()) {
bandwidth_stream << " Send Rate: " << send_data_rate.value() << " Mbit/s" << std::endl;
}
if (auto recv_data_rate = inference_result->recv_data_rate_mbit_sec()) {
bandwidth_stream << " Recv Rate: " << recv_data_rate.value() << " Mbit/s" << std::endl;
}
if (0 != bandwidth_stream.rdbuf()->in_avail()) {
std::cout << " Total bandwidth: " << std::endl;
std::cout << bandwidth_stream.rdbuf();
}
} else {
auto frames_count_exp = inference_result->frames_count();
auto frames_count = ((frames_count_exp) && (m_print_frame_count)) ? frames_count_exp.value() : 0;
print_stdout_single_element<NetworkGroupInferResult>(inference_result.value(), frames_count);
}
if ((inference_result->m_power_measurements.size() != inference_result->m_current_measurements.size()) ||
(inference_result->m_power_measurements.size() != inference_result->m_temp_measurements.size())) {
LOGGER__ERROR("Error found different number of devices between different measurement types");
}
for (const auto &pair : inference_result->m_power_measurements) {
std::stringstream measurement_stream;
if (nullptr != pair.second) {
const auto &data = pair.second->data();
const auto &power_units = pair.second->power_units();
measurement_stream << " Minimum power consumption: " << data.min_value << " " << power_units << std::endl;
measurement_stream << " Average power consumption: " << data.average_value << " " << power_units << std::endl;
measurement_stream << " Maximum power consumption: " << data.max_value << " " << power_units << std::endl;
}
auto current_measure_iter = inference_result->m_current_measurements.find(pair.first);
if ((current_measure_iter != inference_result->m_current_measurements.end()) && (nullptr != current_measure_iter->second)) {
const auto &data = current_measure_iter->second->data();
const auto &power_units = current_measure_iter->second->power_units();
measurement_stream << " Minimum current consumption: " << data.min_value << " " << power_units << std::endl;
measurement_stream << " Average current consumption: " << data.average_value << " " << power_units << std::endl;
measurement_stream << " Maximum current consumption: " << data.max_value << " " << power_units << std::endl;
}
auto temp_measure_iter = inference_result->m_temp_measurements.find(pair.first);
if ((temp_measure_iter != inference_result->m_temp_measurements.end()) && (nullptr != temp_measure_iter->second)) {
measurement_stream << " Minimum chip temperature: " << temp_measure_iter->second->min_value << "°C" << std::endl;
measurement_stream << " Average chip temperature: " << temp_measure_iter->second->average_value << "°C" << std::endl;
measurement_stream << " Maximum chip temperature: " << temp_measure_iter->second->max_value << "°C" << std::endl;
}
if (0 != measurement_stream.rdbuf()->in_avail()) {
std::cout << " Device: " << pair.first << std::endl;
std::cout << measurement_stream.rdbuf();
}
}
// Restore precision and flags
std::cout.flags(original_flags);
std::cout.precision(original_precision);
}
void InferStatsPrinter::write_accumulator_results(std::ofstream &output_stream, AccumulatorPtr accumulator,
const std::string &network_group_name, const std::string &vstream_name, const std::string &elem_name, uint32_t index)
{
const auto &accumulator_result = accumulator->get();
if ((!accumulator_result.count()) || (accumulator_result.count().value() == 0)) {
return;
}
output_stream << network_group_name << ",";
output_stream << vstream_name << ",";
output_stream << accumulator->get_data_type() << ",";
output_stream << elem_name << ",";
output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.mean()) << ",";
output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.min()) << ",";
output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.max()) << ",";
output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.var()) << ",";
output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.sd()) << ",";
output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.mean_sd()) << ",";
if (NO_INDEX != index) {
output_stream << index;
}
output_stream << std::endl;
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file infer_stats_printer.hpp
* @brief Prints the inference stats
**/
#ifndef _HAILO_INFER_STATS_PRINTER_HPP_
#define _HAILO_INFER_STATS_PRINTER_HPP_
#include "inference_result.hpp"
#include "run_command.hpp"
#include <limits>
class InferResultsFormatUtils final {
public:
InferResultsFormatUtils() = delete;
static const uint32_t DEFAULT_FLOATING_POINT_PRECISION = 4;
static std::string format_statistic(const Expected<double> &statistic, uint32_t precision = DEFAULT_FLOATING_POINT_PRECISION);
static std::string format_statistic(const Expected<size_t> &statistic);
static double latency_result_to_ms(std::chrono::nanoseconds latency);
};
class InferStatsPrinter final {
public:
static Expected<InferStatsPrinter> create(const inference_runner_params &params, bool print_running_info = true);
void print(const std::string &network_name, Expected<NetworkGroupInferResult>& inference_result);
void print_benchmark_csv(const std::string &network_name, const NetworkGroupInferResult &hw_inference_result,
const NetworkGroupInferResult &streaming_inference_result, const NetworkGroupInferResult &hw_latency_result);
void print_csv_header();
void print_benchmark_csv_header();
private:
static constexpr uint32_t NO_INDEX = std::numeric_limits<uint32_t>::max();
InferStatsPrinter(const inference_runner_params &params, hailo_status &output_status, bool print_running_info = true);
void print_csv(const std::string &network_name, Expected<NetworkGroupInferResult>& inference_result);
void print_pipeline_elem_stats_csv(const std::string &network_name,
const std::map<std::string, std::map<std::string, AccumulatorPtr>> &inference_result);
void print_pipeline_elem_stats_csv(const std::string &network_name,
const std::map<std::string, std::map<std::string, std::vector<AccumulatorPtr>>> &inference_result);
void print_entire_pipeline_stats_csv(const std::string &network_name,
const std::map<std::string, AccumulatorPtr> &inference_result);
void print_stdout(Expected<NetworkGroupInferResult>& inference_result);
template <typename T>
void print_stdout_single_element(const T &results, size_t frames_count);
// 'index' is only printed if it's not equal to 'NO_INDEX'
static void write_accumulator_results(std::ofstream &output_stream, AccumulatorPtr accumulator,
const std::string &network_name, const std::string &vstream_name, const std::string &elem_name,
uint32_t index=NO_INDEX);
std::ofstream m_results_csv_file;
std::ofstream m_pipeline_stats_csv_file;
std::string m_results_csv_path;
std::string m_pipeline_stats_csv_path;
std::string m_dot_output_path;
bool m_print_frame_count;
};
#endif /* _HAILO_INFER_STATS_PRINTER_HPP_ */

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file inference_progress.cpp
* @brief Show inference progress
**/
#include "inference_progress.hpp"
#include "infer_stats_printer.hpp"
#include "common.hpp"
#include <iostream>
#include <iomanip>
InferProgress::InferProgress(ConfiguredNetworkGroup &configured_network_group, const inference_runner_params &params,
std::chrono::duration<double> print_interval) :
m_configured_network_group(configured_network_group), m_params(params),
m_print_interval(print_interval), m_networks_progress(), m_stop(true) {}
void InferProgress::start()
{
m_stop = false;
m_print_thread = std::thread([this] () {
while (!m_stop.load()) {
print_progress(true);
std::this_thread::sleep_for(m_print_interval);
}
});
}
void InferProgress::finish(bool should_print_progress)
{
m_stop = true;
m_print_thread.join();
if (should_print_progress) {
print_progress(false);
}
}
void InferProgress::print_progress(bool should_reset_cursor)
{
for (auto &network_progress_bar : m_networks_progress) {
std::cout << network_progress_bar->get_progress_text() << std::endl;
}
if (should_reset_cursor) {
CliCommon::reset_cursor(m_networks_progress.size());
}
}
InferProgress::~InferProgress()
{
if (!m_stop.load()) {
finish(false);
}
}
Expected<std::shared_ptr<NetworkProgressBar>> InferProgress::create_network_progress_bar(const std::string &network_name)
{
std::shared_ptr<NetworkProgressBar> network_progress_ber =
make_shared_nothrow<NetworkProgressBar>(m_configured_network_group, m_params, network_name);
CHECK_NOT_NULL_AS_EXPECTED(network_progress_ber, HAILO_OUT_OF_HOST_MEMORY);
{
// We create NetworkProgressBar from different threads
std::unique_lock<std::mutex> lock(m_mutex);
m_networks_progress.push_back(network_progress_ber);
}
auto prog_bar_cpy = network_progress_ber;
return prog_bar_cpy;
}
NetworkProgressBar::NetworkProgressBar(ConfiguredNetworkGroup &configured_network_group,
const inference_runner_params &params, const std::string &network_name) :
m_network_name(network_name), m_configured_network_group(configured_network_group), m_params(params),
m_progress_count(0), m_start(std::chrono::steady_clock::now()) // NetworkProgressBar sets start time to its creation time
{}
std::string NetworkProgressBar::get_progress_text()
{
std::stringstream res;
auto elapsed_time = std::chrono::duration<double>(std::chrono::steady_clock::now() - m_start).count();
auto progress_count = m_progress_count.load();
auto fps = progress_count / elapsed_time;
auto eta = std::chrono::seconds(0);
if (0 == m_params.time_to_run) {
eta = std::chrono::seconds(static_cast<uint32_t>(static_cast<double>(m_params.frames_count - progress_count) / fps));
} else {
eta = std::chrono::seconds(std::max(static_cast<int32_t>(0),
static_cast<int32_t>(std::round(m_params.time_to_run - elapsed_time))));
}
// Set precision and flags
res << std::setprecision(2) << std::fixed;
uint32_t progress_percent = 0;
if (0 == m_params.time_to_run) {
progress_percent = 100 * progress_count / m_params.frames_count;
} else {
progress_percent = std::min(static_cast<uint32_t>(100 * elapsed_time / m_params.time_to_run), static_cast<uint32_t>(100));
}
res << "Network " << m_network_name << ": " << progress_percent << "% | " << progress_count;
if (0 == m_params.time_to_run) {
res << "/" << m_params.frames_count;
}
if (!m_params.measure_latency) {
res << " | FPS: " << fps;
} else {
double avg_hw_latency = 0;
auto latency_expected = m_configured_network_group.get_latency_measurement(m_network_name);
if (latency_expected) {
avg_hw_latency = InferResultsFormatUtils::latency_result_to_ms(latency_expected.release().avg_hw_latency);
}
if (avg_hw_latency > 0) {
res << " | HW Latency: " << avg_hw_latency << " ms";
}
else {
res << " | HW Latency: NaN";
}
}
res << " | ETA: " << CliCommon::duration_to_string(eta) << std::flush;
return res.str();
}
void NetworkProgressBar::make_progress()
{
++m_progress_count;
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file inference_progress.hpp
* @brief Show inference progress
**/
#ifndef _HAILO_INFERENCE_PROGRESS_HPP_
#define _HAILO_INFERENCE_PROGRESS_HPP_
#include "hailortcli.hpp"
#include "run_command.hpp"
#include "hailo/network_group.hpp"
#include "CLI/CLI.hpp"
class NetworkProgressBar final {
public:
NetworkProgressBar(ConfiguredNetworkGroup &configured_network_group,
const inference_runner_params &params, const std::string &network_name);
void make_progress();
std::string get_progress_text();
private:
const std::string m_network_name;
ConfiguredNetworkGroup &m_configured_network_group;
const inference_runner_params m_params;
std::atomic<uint32_t> m_progress_count;
std::chrono::time_point<std::chrono::steady_clock> m_start;
};
class InferProgress final {
public:
InferProgress(ConfiguredNetworkGroup &configured_network_group,
const inference_runner_params &params, std::chrono::duration<double> print_interval);
~InferProgress();
Expected<std::shared_ptr<NetworkProgressBar>> create_network_progress_bar(const std::string &network_name);
void start();
void finish(bool should_print_progress = true);
private:
void print_progress(bool should_reset_cursor);
ConfiguredNetworkGroup &m_configured_network_group;
const inference_runner_params m_params;
std::chrono::duration<double> m_print_interval;
std::vector<std::shared_ptr<NetworkProgressBar>> m_networks_progress;
std::atomic_bool m_stop;
std::thread m_print_thread;
std::mutex m_mutex;
};
#endif /* _HAILO_INFERENCE_PROGRESS_HPP_ */

View File

@@ -0,0 +1,326 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file inference_result.hpp
* @brief hold inference result
**/
#ifndef _HAILO_INFER_RESULT_
#define _HAILO_INFER_RESULT_
#include "power_measurement_command.hpp"
#include "temp_measurement.hpp"
#include "hailo/runtime_statistics.hpp"
#include "hailo/vstream.hpp"
static constexpr double MBIT_PER_BYTE = 8.0f / 1000.0f / 1000.0f;
struct NetworkInferResult
{
public:
NetworkInferResult(size_t frames_count = 0, size_t total_send_frame_size = 0, size_t total_recv_frame_size = 0) :
m_frames_count(frames_count),
m_total_send_frame_size(total_send_frame_size),
m_total_recv_frame_size(total_recv_frame_size),
m_infer_duration(nullptr),
m_hw_latency(nullptr),
m_overall_latency(nullptr)
{}
Expected<double> infer_duration() const{
if (!m_infer_duration) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
auto infer_duration_cpy = *m_infer_duration;
return infer_duration_cpy;
}
Expected<double> fps() const
{
if (!m_infer_duration) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
return static_cast<double>(m_frames_count) / *m_infer_duration;
}
Expected<double> send_data_rate_mbit_sec() const
{
if (!m_infer_duration) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
return (static_cast<double>(m_frames_count * m_total_send_frame_size) / *m_infer_duration) * MBIT_PER_BYTE;
}
Expected<double> recv_data_rate_mbit_sec() const
{
if (!m_infer_duration) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
return (static_cast<double>(m_frames_count * m_total_recv_frame_size) / *m_infer_duration) * MBIT_PER_BYTE;
}
Expected<std::chrono::nanoseconds> hw_latency() const
{
if (!m_hw_latency) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
std::chrono::nanoseconds latency_cpy = *m_hw_latency;
return latency_cpy;
}
Expected<std::chrono::nanoseconds> overall_latency() const
{
if (!m_overall_latency) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
std::chrono::nanoseconds latency_cpy = *m_overall_latency;
return latency_cpy;
}
size_t m_frames_count;
size_t m_total_send_frame_size;
size_t m_total_recv_frame_size;
// TODO: change to optional
std::unique_ptr<double> m_infer_duration;
std::unique_ptr<std::chrono::nanoseconds> m_hw_latency;
std::unique_ptr<std::chrono::nanoseconds> m_overall_latency;
};
struct NetworkGroupInferResult
{
public:
NetworkGroupInferResult(std::map<std::string, NetworkInferResult> &&result_per_network = {}) :
m_result_per_network(std::move(result_per_network)),
m_power_measurements(),
m_current_measurements(),
m_temp_measurements(),
m_fps_accumulators(),
m_latency_accumulators(),
m_queue_size_accumulators(),
m_pipeline_latency_accumulators()
{}
Expected<double> infer_duration(const std::string &network_name = "") const
{
if (network_name.empty()) {
// We set m_infer_duration to be the max of all durations
double max_duration = 0;
for (auto &network_result_pair : m_result_per_network) {
auto duration_per_network = network_result_pair.second.infer_duration();
if(!duration_per_network) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
max_duration = std::max(max_duration, duration_per_network.value());
}
return max_duration;
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
return m_result_per_network.at(network_name).infer_duration();
}
Expected<double> fps(const std::string &network_name = "") const
{
if (network_name.empty()) {
double acc_fps = 0;
for (auto &network_result_pair : m_result_per_network) {
auto fps_per_network = network_result_pair.second.fps();
if (!fps_per_network) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
acc_fps += fps_per_network.value();
}
return (acc_fps / static_cast<double>(m_result_per_network.size()));
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
return m_result_per_network.at(network_name).fps();
}
Expected<double> send_data_rate_mbit_sec(const std::string &network_name = "") const
{
if (network_name.empty()) {
double acc_send_data_rate_mbit_sec = 0;
for (auto &network_result_pair : m_result_per_network) {
auto send_data_rate_mbit_sec_per_network = network_result_pair.second.send_data_rate_mbit_sec();
if(!send_data_rate_mbit_sec_per_network) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
acc_send_data_rate_mbit_sec += send_data_rate_mbit_sec_per_network.value();
}
return acc_send_data_rate_mbit_sec;
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
return m_result_per_network.at(network_name).send_data_rate_mbit_sec();
}
Expected<double> recv_data_rate_mbit_sec(const std::string &network_name = "") const
{
if (network_name.empty()) {
double acc_recv_data_rate_mbit_sec = 0;
for (auto &network_result_pair : m_result_per_network) {
auto recv_data_rate_mbit_sec_per_network = network_result_pair.second.recv_data_rate_mbit_sec();
if(!recv_data_rate_mbit_sec_per_network) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
acc_recv_data_rate_mbit_sec += recv_data_rate_mbit_sec_per_network.value();
}
return acc_recv_data_rate_mbit_sec;
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
return m_result_per_network.at(network_name).send_data_rate_mbit_sec();
}
Expected<std::chrono::nanoseconds> hw_latency(const std::string &network_name = "") const
{
if (network_name.empty()) {
std::chrono::nanoseconds acc_hw_latency(0);
for (auto &network_result_pair : m_result_per_network) {
auto hw_latency_per_network = network_result_pair.second.hw_latency();
if(!hw_latency_per_network) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
acc_hw_latency += hw_latency_per_network.value();
}
return static_cast<std::chrono::nanoseconds>(acc_hw_latency / m_result_per_network.size());
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
return m_result_per_network.at(network_name).hw_latency();
}
Expected<std::chrono::nanoseconds> overall_latency(const std::string &network_name = "") const
{
if (network_name.empty()) {
std::chrono::nanoseconds acc_overall_latency(0);
for (auto &network_result_pair : m_result_per_network) {
auto overall_latency_per_network = network_result_pair.second.overall_latency();
if(!overall_latency_per_network) {
return make_unexpected(HAILO_NOT_AVAILABLE);
}
acc_overall_latency += overall_latency_per_network.value();
}
return static_cast<std::chrono::nanoseconds>(acc_overall_latency / m_result_per_network.size());
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
return m_result_per_network.at(network_name).overall_latency();
}
Expected<size_t> frames_count(const std::string &network_name = "") const
{
if (network_name.empty()) {
if (1 == m_result_per_network.size()) {
// If there is only one network, return its frames_count
auto frames_count_cpy = m_result_per_network.begin()->second.m_frames_count;
return frames_count_cpy;
}
return make_unexpected(HAILO_NOT_AVAILABLE);
}
CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
"There is no results for network {}", network_name);
auto frames_count_cpy = m_result_per_network.at(network_name).m_frames_count;
return frames_count_cpy;
}
// <network_name, network_inference_results>
std::map<std::string, NetworkInferResult> m_result_per_network;
// <device_id, measurement>
// TODO: create a struct contianing all device measurements, and keep only one map
std::map<std::string, std::unique_ptr<LongPowerMeasurement>> m_power_measurements;
std::map<std::string, std::unique_ptr<LongPowerMeasurement>> m_current_measurements;
std::map<std::string, std::unique_ptr<TempMeasurementData>> m_temp_measurements;
// <vstream_name, accumulator>
std::map<std::string, std::map<std::string, AccumulatorPtr>> m_fps_accumulators;
std::map<std::string, std::map<std::string, AccumulatorPtr>> m_latency_accumulators;
std::map<std::string, std::map<std::string, std::vector<AccumulatorPtr>>> m_queue_size_accumulators;
std::map<std::string, AccumulatorPtr> m_pipeline_latency_accumulators;
void update_pipeline_stats(const std::map<std::string, std::vector<std::reference_wrapper<InputVStream>>> &inputs_per_network,
const std::map<std::string, std::vector<std::reference_wrapper<OutputVStream>>> &outputs_per_network)
{
for (const auto &inputs_pair : inputs_per_network) {
for (const auto &in_vstream : inputs_pair.second) {
update_accumulator_map(m_fps_accumulators, in_vstream.get().name(), in_vstream.get().get_fps_accumulators());
update_accumulator_map(m_latency_accumulators, in_vstream.get().name(), in_vstream.get().get_latency_accumulators());
update_accumulator_map(m_queue_size_accumulators, in_vstream.get().name(), in_vstream.get().get_queue_size_accumulators());
const auto pipeline_latency_accumulator = in_vstream.get().get_pipeline_latency_accumulator();
if (nullptr != pipeline_latency_accumulator) {
m_pipeline_latency_accumulators.emplace(in_vstream.get().name(), pipeline_latency_accumulator);
}
}
}
for (const auto &outputs_pair : outputs_per_network) {
for (const auto &out_vstream : outputs_pair.second) {
update_accumulator_map(m_fps_accumulators, out_vstream.get().name(), out_vstream.get().get_fps_accumulators());
update_accumulator_map(m_latency_accumulators, out_vstream.get().name(), out_vstream.get().get_latency_accumulators());
update_accumulator_map(m_queue_size_accumulators, out_vstream.get().name(), out_vstream.get().get_queue_size_accumulators());
const auto pipeline_latency_accumulator = out_vstream.get().get_pipeline_latency_accumulator();
if (nullptr != pipeline_latency_accumulator) {
m_pipeline_latency_accumulators.emplace(out_vstream.get().name(), pipeline_latency_accumulator);
}
}
}
}
void update_pipeline_stats(const std::map<std::string, std::vector<std::reference_wrapper<InputStream>>> &/*inputs_per_network*/,
const std::map<std::string, std::vector<std::reference_wrapper<OutputStream>>> &/*outputs_per_network*/)
{
// Overloading fow hw_object inference - not using any pipelines so nothing to update
}
void initialize_measurements(const std::vector<std::reference_wrapper<Device>> &devices)
{
for (const auto &device : devices) {
m_power_measurements.emplace(device.get().get_dev_id(), std::unique_ptr<LongPowerMeasurement>{});
m_current_measurements.emplace(device.get().get_dev_id(), std::unique_ptr<LongPowerMeasurement>{});
m_temp_measurements.emplace(device.get().get_dev_id(), std::unique_ptr<TempMeasurementData>{});
}
}
hailo_status set_power_measurement(const std::string &device_id, std::unique_ptr<LongPowerMeasurement> &&power_measure)
{
auto iter = m_power_measurements.find(device_id);
CHECK(m_power_measurements.end() != iter, HAILO_INVALID_ARGUMENT);
iter->second = std::move(power_measure);
return HAILO_SUCCESS;
}
hailo_status set_current_measurement(const std::string &device_id, std::unique_ptr<LongPowerMeasurement> &&current_measure)
{
auto iter = m_current_measurements.find(device_id);
CHECK(m_current_measurements.end() != iter, HAILO_INVALID_ARGUMENT);
iter->second = std::move(current_measure);
return HAILO_SUCCESS;
}
hailo_status set_temp_measurement(const std::string &device_id, std::unique_ptr<TempMeasurementData> &&temp_measure)
{
auto iter = m_temp_measurements.find(device_id);
CHECK(m_temp_measurements.end() != iter, HAILO_INVALID_ARGUMENT);
iter->second = std::move(temp_measure);
return HAILO_SUCCESS;
}
private:
template <typename K, typename V>
static void update_accumulator_map(std::map<K, V> &map,
const K &key, const V &value)
{
if (value.size() == 0) {
return;
}
map.emplace(key, value);
}
};
#endif /* _HAILO_INFER_RESULT_ */

View File

@@ -0,0 +1,161 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file parse_hef_command.cpp
* @brief Parses HEF and print info to stdout
**/
#include "parse_hef_command.hpp"
#include "common/filesystem.hpp"
#include "hailo/hailort_common.hpp"
#define TAB (" ")
static std::string add_tabs(uint8_t count)
{
// Each TAB counts as 4 spaces
std::string res = "";
for (uint8_t i = 0; i < count; i++) {
res = res + TAB;
}
return res;
}
static std::string get_shape_str(const hailo_stream_info_t &stream_info)
{
switch (stream_info.format.order)
{
case HAILO_FORMAT_ORDER_HAILO_NMS:
return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
"(number of classes: " + std::to_string(stream_info.nms_info.number_of_classes) +
", max_bboxes_per_class: "+ std::to_string(stream_info.nms_info.max_bboxes_per_class) + ")";
case HAILO_FORMAT_ORDER_NC:
return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
"(" + std::to_string(stream_info.hw_shape.features) + ")";
case HAILO_FORMAT_ORDER_NHW:
return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
"(" + std::to_string(stream_info.hw_shape.height) + "x" + std::to_string(stream_info.hw_shape.width) + ")";
default:
return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
"(" + std::to_string(stream_info.hw_shape.height) + "x" + std::to_string(stream_info.hw_shape.width) +
"x" + std::to_string(stream_info.hw_shape.features) + ")";
}
}
static std::string get_shape_str(const hailo_vstream_info_t &vstream_info)
{
switch (vstream_info.format.order)
{
case HAILO_FORMAT_ORDER_HAILO_NMS:
return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
"(number of classes: " + std::to_string(vstream_info.nms_shape.number_of_classes) +
", max_bboxes_per_class: " + std::to_string(vstream_info.nms_shape.max_bboxes_per_class) + ")";
case HAILO_FORMAT_ORDER_NC:
return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
"(" + std::to_string(vstream_info.shape.features) + ")";
case HAILO_FORMAT_ORDER_NHW:
return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
"(" +std::to_string(vstream_info.shape.height) + "x" + std::to_string(vstream_info.shape.width) + ")";
default:
return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
"(" + std::to_string(vstream_info.shape.height) + "x" + std::to_string(vstream_info.shape.width) + "x" +
std::to_string(vstream_info.shape.features) + ")";
}
}
ParseHefCommand::ParseHefCommand(CLI::App &parent_app) :
Command(parent_app.add_subcommand("parse-hef", "Parse HEF to get information about its components"))
{
m_app->add_option("hef", m_hef_path, "An existing HEF file/directory path")
->check(CLI::ExistingFile | CLI::ExistingDirectory)
->required();
m_app->add_flag("--parse-streams", m_parse_streams, "Parse stream infos")->default_val(false);
m_app->add_flag("--parse-vstreams", m_parse_vstreams, "Parse vstream infos")->default_val(true);
}
hailo_status ParseHefCommand::execute()
{
auto is_dir = Filesystem::is_directory(m_hef_path.c_str());
CHECK_EXPECTED_AS_STATUS(is_dir, "Failed checking if path is directory");
if (is_dir.value()){
return ParseHefCommand::parse_hefs_infos_dir(m_hef_path, m_parse_streams, m_parse_vstreams);
} else {
return ParseHefCommand::parse_hefs_info(m_hef_path, m_parse_streams, m_parse_vstreams);
}
}
hailo_status ParseHefCommand::parse_hefs_info(const std::string &hef_path, bool stream_infos, bool vstream_infos)
{
auto hef_exp = Hef::create(hef_path);
CHECK_EXPECTED_AS_STATUS(hef_exp, "Failed to parse HEF");
auto hef = hef_exp.release();
auto network_group_infos = hef.get_network_groups_infos();
CHECK_EXPECTED_AS_STATUS(network_group_infos);
for (auto &network_group_info : network_group_infos.release()) {
auto contexts_str = (network_group_info.is_multi_context ? "Multi Context" : "Single Context");
std::cout << "Network group name: " << network_group_info.name << " (" << contexts_str << ")" << std::endl;
auto network_infos = hef.get_network_infos(network_group_info.name);
CHECK_EXPECTED_AS_STATUS(network_infos, "Failed to parse networks infos");
for (auto &network_info : network_infos.value()) {
std::cout << add_tabs(1) << "Network name: " << network_info.name << std::endl;
if (stream_infos) {
std::cout << add_tabs(2) << "Stream infos:" << std::endl;
auto input_stream_infos = hef.get_input_stream_infos(network_info.name);
CHECK_EXPECTED_AS_STATUS(input_stream_infos, "Failed to parse input stream infos");
for (auto &stream_info : input_stream_infos.value()) {
auto shape_str = get_shape_str(stream_info);
std::cout << add_tabs(3) << "Input " << stream_info.name << " " << shape_str << std::endl;
}
auto output_stream_infos = hef.get_output_stream_infos(network_info.name);
CHECK_EXPECTED_AS_STATUS(output_stream_infos, "Failed to parse output stream infos");
for (auto &stream_info : output_stream_infos.value()) {
auto shape_str = get_shape_str(stream_info);
std::cout << add_tabs(3) << "Output " << stream_info.name << " " << shape_str << std::endl;
}
}
if (vstream_infos) {
std::cout << add_tabs(2) << "VStream infos:" << std::endl;
auto input_vstream_infos = hef.get_input_vstream_infos(network_info.name);
CHECK_EXPECTED_AS_STATUS(input_vstream_infos, "Failed to parse input vstream infos");
for (auto &vstream_info : input_vstream_infos.value()) {
auto shape_str = get_shape_str(vstream_info);
std::cout << add_tabs(3) << "Input " << vstream_info.name << " " << shape_str << std::endl;
}
auto output_vstream_infos = hef.get_output_vstream_infos(network_info.name);
CHECK_EXPECTED_AS_STATUS(output_vstream_infos, "Failed to parse output vstream infos");
for (auto &vstream_info : output_vstream_infos.value()) {
auto shape_str = get_shape_str(vstream_info);
std::cout << add_tabs(3) << "Output " << vstream_info.name << " " << shape_str << std::endl;
}
}
}
}
std::cout << std::endl;
return HAILO_SUCCESS;
}
hailo_status ParseHefCommand::parse_hefs_infos_dir(const std::string &hef_path, bool stream_infos, bool vstream_infos)
{
bool contains_hef = false;
std::string hef_dir = hef_path;
const auto files = Filesystem::get_files_in_dir_flat(hef_dir);
CHECK_EXPECTED_AS_STATUS(files);
for (const auto &full_path : files.value()) {
if (Filesystem::has_suffix(full_path, ".hef")) {
contains_hef = true;
std::cout << std::string(80, '*') << std::endl << "Parsing " << full_path << ":"<< std::endl;
auto status = ParseHefCommand::parse_hefs_info(full_path, stream_infos, vstream_infos);
CHECK_SUCCESS(status, "Failed to parse HEF {}", full_path);
}
}
CHECK(contains_hef, HAILO_INVALID_ARGUMENT, "No HEF files were found in the directory: {}", hef_dir);
return HAILO_SUCCESS;
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file parse_hef_command.hpp
* @brief Parse HEF and print metadata to stdout
**/
#ifndef _HAILO_PARSE_COMMAND_COMMAND_HPP_
#define _HAILO_PARSE_COMMAND_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "hailo/buffer.hpp"
#include "CLI/CLI.hpp"
class ParseHefCommand : public Command {
public:
explicit ParseHefCommand(CLI::App &parent_app);
virtual hailo_status execute() override;
private:
static hailo_status parse_hefs_infos_dir(const std::string &hef_path, bool stream_infos, bool vstream_infos);
static hailo_status parse_hefs_info(const std::string &hef_path, bool stream_infos, bool vstream_infos);
std::string m_hef_path;
bool m_parse_streams;
bool m_parse_vstreams;
};
#endif /* _HAILO_PARSE_COMMAND_COMMAND_HPP_ */

View File

@@ -0,0 +1,283 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file power_measurement_command.cpp
* @brief Measure power of Hailo chip
**/
#include "power_measurement_command.hpp"
#include <thread>
#define POWER_MEASUREMENT_DELAY_MS(__sample_period, __average_factor) \
(static_cast<uint32_t>((__sample_period) / 1000.0 * (__average_factor) * 2 * 1.2))
PowerMeasurementSubcommand::PowerMeasurementSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("measure-power", "Measures power consumption")), m_params(),
m_power_measurement_duration(0)
{
m_app->add_option("--duration", m_power_measurement_duration, "The duration in seconds to measure power consumption")
->check(CLI::Validator(CLI::PositiveNumber));
CLI::Option *sampling_period = m_app->add_option("--sampling-period", m_params.sampling_period, "Sampling Period")
->needs("--duration");
CLI::Option *averaging_factor = m_app->add_option("--averaging-factor", m_params.averaging_factor, "Averaging Factor")
->needs("--duration");
init_dvm_option(m_app->add_option("--dvm", m_params.dvm_option,
"DVM type. \n\
Which DVM will be measured. Default (AUTO) will be different according to the board: \n\
Default (AUTO) for EVB is an approximation to the total power consumption of the chip in PCIe setups. \n\
It sums VDD_CORE, MIPI_AVDD and AVDD_H. Only POWER can measured with this option. \n\
Default (AUTO) for platforms supporting current monitoring (such as M.2 and mPCIe): OVERCURRENT_PROTECTION"));
init_measurement_type_option(m_app->add_option("--type", m_params.measurement_type, "Power Measurement type"));
init_sampling_period_option(sampling_period);
init_averaging_factor_option(averaging_factor);
}
void PowerMeasurementSubcommand::init_dvm_option(CLI::Option *dvm_option)
{
dvm_option->transform(HailoCheckedTransformer<hailo_dvm_options_t>({
{ "AUTO", HAILO_DVM_OPTIONS_AUTO },
{ "VDD_CORE", HAILO_DVM_OPTIONS_VDD_CORE },
{ "VDD_IO", HAILO_DVM_OPTIONS_VDD_IO },
{ "MIPI_AVDD", HAILO_DVM_OPTIONS_MIPI_AVDD },
{ "MIPI_AVDD_H", HAILO_DVM_OPTIONS_MIPI_AVDD_H },
{ "USB_AVDD_IO", HAILO_DVM_OPTIONS_USB_AVDD_IO },
{ "VDD_TOP", HAILO_DVM_OPTIONS_VDD_TOP },
{ "USB_AVDD_IO_HV", HAILO_DVM_OPTIONS_USB_AVDD_IO_HV },
{ "AVDD_H", HAILO_DVM_OPTIONS_AVDD_H },
{ "SDIO_VDD_IO", HAILO_DVM_OPTIONS_SDIO_VDD_IO },
{ "OVERCURRENT_PROTECTION", HAILO_DVM_OPTIONS_OVERCURRENT_PROTECTION }
}))
->default_val("AUTO");
}
void PowerMeasurementSubcommand::init_measurement_type_option(CLI::Option *measurement_type)
{
measurement_type->transform(HailoCheckedTransformer<hailo_power_measurement_types_t>({
{ "AUTO", HAILO_POWER_MEASUREMENT_TYPES__AUTO },
{ "SHUNT_VOLTAGE", HAILO_POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE },
{ "BUS_VOLTAGE", HAILO_POWER_MEASUREMENT_TYPES__BUS_VOLTAGE },
{ "POWER", HAILO_POWER_MEASUREMENT_TYPES__POWER },
{ "CURRENT", HAILO_POWER_MEASUREMENT_TYPES__CURRENT }
}))
->default_val("AUTO");
}
void PowerMeasurementSubcommand::init_sampling_period_option(CLI::Option *sampling_period)
{
sampling_period->default_val(1100)
->transform(CLI::IsMember({140, 204, 332, 588, 1100, 2116, 4156, 8244}));
}
void PowerMeasurementSubcommand::init_averaging_factor_option(CLI::Option *averaging_factor)
{
averaging_factor->default_val(256)
->transform(CLI::IsMember({1, 4, 16, 64, 128, 256, 512, 1024}));
}
hailo_status PowerMeasurementSubcommand::execute_on_device(Device &device)
{
hailo_status status = HAILO_UNINITIALIZED;
if (0 < m_power_measurement_duration) {
status = run_long_power_measurement(device);
} else {
status = run_single_power_measurement(device);
}
if (HAILO_SUCCESS != status) {
std::cerr << "Failed power measurement, status " << status << std::endl;
return status;
}
return HAILO_SUCCESS;
}
Expected<LongPowerMeasurement> PowerMeasurementSubcommand::start_power_measurement(Device &device,
hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type, uint32_t sampling_period,
uint32_t averaging_factor)
{
hailo_sampling_period_t sampling_period_enum = get_sampling_period(
sampling_period);
if (HAILO_SAMPLING_PERIOD_MAX_ENUM == sampling_period_enum) {
std::cerr << "Failed to parse sampling period: " << sampling_period << std::endl;
return make_unexpected(HAILO_NOT_FOUND);
}
hailo_averaging_factor_t averaging_factor_enum = get_averaging_factor(
averaging_factor);
if (HAILO_AVERAGE_FACTOR_MAX_ENUM == averaging_factor_enum) {
std::cerr << "Failed to parse averaging factor: " << averaging_factor << std::endl;
return make_unexpected(HAILO_NOT_FOUND);
}
hailo_status status = hailo_stop_power_measurement(reinterpret_cast<hailo_device>(&device));
if (HAILO_SUCCESS != status) {
std::cerr << "Failed initial power measurement stop, status " << status << std::endl;
return make_unexpected(status);
}
status = hailo_set_power_measurement(reinterpret_cast<hailo_device>(&device), 0, dvm, measurement_type);
if (HAILO_SUCCESS != status) {
std::cerr << "Failed to set power measurement parameters, status " << status << std::endl;
return make_unexpected(status);
}
uint32_t measurement_delay = POWER_MEASUREMENT_DELAY_MS(sampling_period, averaging_factor);
// There is no logical way that measurement delay can be 0 - because sampling_period and averaging_factor cant be 0
// Hence if it is 0 - it means it was 0.xx and we want to round up to 1 in that case
if (0 == measurement_delay) {
measurement_delay = 1;
}
status = hailo_start_power_measurement(reinterpret_cast<hailo_device>(&device), measurement_delay,
averaging_factor_enum, sampling_period_enum);
if (HAILO_SUCCESS != status) {
std::cerr << "Failed to start power measurement, status " << status << std::endl;
return make_unexpected(status);
}
return LongPowerMeasurement(device, measurement_type);
}
LongPowerMeasurement::LongPowerMeasurement(Device &device,
hailo_power_measurement_types_t measurement_type) : m_device(device),
m_measurement_type(measurement_type), m_data(), m_power_units()
{}
hailo_status LongPowerMeasurement::stop()
{
hailo_status status = hailo_stop_power_measurement(reinterpret_cast<hailo_device>(&m_device));
if (HAILO_SUCCESS != status) {
std::cerr << "Failed to stop power measurement, status " << status << std::endl;
return status;
}
status = hailo_get_power_measurement(reinterpret_cast<hailo_device>(&m_device), 0, true, &m_data);
if (HAILO_SUCCESS != status) {
std::cerr << "Failed to get power measurement results, status " << status << std::endl;
return status;
}
const char *power_units = PowerMeasurementSubcommand::get_power_units(m_measurement_type);
if (nullptr == power_units) {
std::cerr << "Failed to get power measurement units of type " << m_measurement_type << std::endl;
return HAILO_NOT_FOUND;
}
m_power_units = power_units;
return HAILO_SUCCESS;
}
hailo_sampling_period_t PowerMeasurementSubcommand::get_sampling_period(uint32_t sampling_period)
{
switch (sampling_period) {
case 140:
return HAILO_SAMPLING_PERIOD_140US;
case 204:
return HAILO_SAMPLING_PERIOD_204US;
case 332:
return HAILO_SAMPLING_PERIOD_332US;
case 588:
return HAILO_SAMPLING_PERIOD_588US;
case 1100:
return HAILO_SAMPLING_PERIOD_1100US;
case 2116:
return HAILO_SAMPLING_PERIOD_2116US;
case 4156:
return HAILO_SAMPLING_PERIOD_4156US;
case 8244:
return HAILO_SAMPLING_PERIOD_8244US;
default:
return HAILO_SAMPLING_PERIOD_MAX_ENUM;
}
}
hailo_averaging_factor_t PowerMeasurementSubcommand::get_averaging_factor(uint32_t averaging_factor)
{
switch (averaging_factor) {
case 1:
return HAILO_AVERAGE_FACTOR_1;
case 4:
return HAILO_AVERAGE_FACTOR_4;
case 16:
return HAILO_AVERAGE_FACTOR_16;
case 64:
return HAILO_AVERAGE_FACTOR_64;
case 128:
return HAILO_AVERAGE_FACTOR_128;
case 256:
return HAILO_AVERAGE_FACTOR_256;
case 512:
return HAILO_AVERAGE_FACTOR_512;
case 1024:
return HAILO_AVERAGE_FACTOR_1024;
default:
return HAILO_AVERAGE_FACTOR_MAX_ENUM;
}
}
hailo_status PowerMeasurementSubcommand::run_long_power_measurement(Device &device)
{
auto long_power_measurement = start_power_measurement(device, m_params.dvm_option,
m_params.measurement_type, m_params.sampling_period, m_params.averaging_factor);
if (!long_power_measurement) {
return long_power_measurement.status();
}
std::this_thread::sleep_for(std::chrono::seconds(m_power_measurement_duration));
hailo_status status = long_power_measurement.value().stop();
if (HAILO_SUCCESS != status) {
return status;
}
const hailo_power_measurement_data_t &measurement_data = long_power_measurement.value().data();
const std::string &power_units = long_power_measurement.value().power_units();
std::cout << "Measuring power consumption over " << m_power_measurement_duration << " seconds:" << std::endl;
std::cout << "Total samples: " << measurement_data.total_number_of_samples << std::endl;
std::cout << "Max value (" << power_units << "): " << measurement_data.max_value << std::endl;
std::cout << "Min value (" << power_units << "): " << measurement_data.min_value << std::endl;
std::cout << "Average value (" << power_units << "): " << measurement_data.average_value << std::endl;
std::cout << "Average time per sample (ms): " << measurement_data.average_time_value_milliseconds << std::endl;
return HAILO_SUCCESS;
}
const char *PowerMeasurementSubcommand::get_power_units(hailo_power_measurement_types_t measurement_type)
{
switch (measurement_type) {
case HAILO_POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE:
case HAILO_POWER_MEASUREMENT_TYPES__BUS_VOLTAGE:
return "mV";
case HAILO_POWER_MEASUREMENT_TYPES__AUTO:
case HAILO_POWER_MEASUREMENT_TYPES__POWER:
return "W";
case HAILO_POWER_MEASUREMENT_TYPES__CURRENT:
return "mA";
default:
return nullptr;
};
}
hailo_status PowerMeasurementSubcommand::run_single_power_measurement(Device &device)
{
float32_t measurement = 0.0f;
hailo_status status = hailo_power_measurement(reinterpret_cast<hailo_device>(&device), m_params.dvm_option, m_params.measurement_type,
&measurement);
if (HAILO_SUCCESS != status) {
std::cerr << "Failed to get power measurement results, status " << status << std::endl;
return status;
}
const char *power_units = get_power_units(m_params.measurement_type);
if (nullptr == power_units) {
std::cerr << "Failed to get power measurement units of type " << m_params.measurement_type << std::endl;
return HAILO_NOT_FOUND;
}
std::cout << "Current power consumption (" << power_units << "): " << measurement << std::endl;
return HAILO_SUCCESS;
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file power_measurement_command.hpp
* @brief Measure power of Hailo chip
**/
#ifndef _HAILO_POWER_MEASUREMENT_COMMAND_HPP_
#define _HAILO_POWER_MEASUREMENT_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "CLI/CLI.hpp"
class LongPowerMeasurement final {
public:
LongPowerMeasurement(Device &device, hailo_power_measurement_types_t measurement_type);
~LongPowerMeasurement() = default;
hailo_status stop();
const hailo_power_measurement_data_t &data() const
{
return m_data;
}
const std::string &power_units() const
{
return m_power_units;
}
private:
Device &m_device;
hailo_power_measurement_types_t m_measurement_type;
hailo_power_measurement_data_t m_data;
std::string m_power_units;
};
class PowerMeasurementSubcommand final : public DeviceCommand {
public:
explicit PowerMeasurementSubcommand(CLI::App &parent_app);
static Expected<LongPowerMeasurement> start_power_measurement(Device &device,
hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type,
uint32_t sampling_period, uint32_t averaging_factor);
static void init_sampling_period_option(CLI::Option *sampling_period);
static void init_averaging_factor_option(CLI::Option *averaging_factor);
static hailo_sampling_period_t get_sampling_period(uint32_t sampling_period);
static hailo_averaging_factor_t get_averaging_factor(uint32_t averaging_factor);
static const char *get_power_units(hailo_power_measurement_types_t measurement_type);
protected:
hailo_status execute_on_device(Device &device) override;
private:
struct power_measurement_params {
hailo_dvm_options_t dvm_option;
hailo_power_measurement_types_t measurement_type;
uint32_t sampling_period;
uint32_t averaging_factor;
};
power_measurement_params m_params;
uint32_t m_power_measurement_duration;
static void init_dvm_option(CLI::Option *dvm_option);
static void init_measurement_type_option(CLI::Option *measurement_type);
hailo_status run_long_power_measurement(Device &device);
hailo_status run_single_power_measurement(Device &device);
};
#endif /* _HAILO_POWER_MEASUREMENT_COMMAND_HPP_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file run_command.hpp
* @brief Run inference on hailo device
**/
#ifndef _HAILO_RUN_COMMAND_HPP_
#define _HAILO_RUN_COMMAND_HPP_
#include "hailortcli.hpp"
#include "power_measurement_command.hpp"
#include "temp_measurement.hpp"
#include "CLI/CLI.hpp"
#include "inference_result.hpp"
enum class InferMode {
STREAMING,
HW_ONLY,
};
struct transformation_params {
bool transform;
bool quantized;
hailo_format_type_t format_type;
};
struct measure_power_params {
bool measure_power;
bool measure_current;
uint32_t sampling_period;
uint32_t averaging_factor;
};
struct pipeline_stats_measurement_params {
bool measure_elem_fps;
bool measure_elem_latency;
bool measure_elem_queue_size;
bool measure_vstream_fps;
bool measure_vstream_latency;
std::string pipeline_stats_output_path;
};
struct runtime_data_params {
bool download_runtime_data;
std::string runtime_data_output_path;
};
struct inference_runner_params {
hailo_device_params device_params;
std::string hef_path;
uint32_t frames_count;
uint32_t time_to_run;
InferMode mode;
std::string csv_output;
std::vector<std::string> inputs_name_and_file_path;
bool measure_latency;
bool measure_overall_latency;
bool show_progress;
transformation_params transform;
measure_power_params power_measurement;
uint16_t batch_size;
hailo_power_mode_t power_mode;
pipeline_stats_measurement_params pipeline_stats;
runtime_data_params runtime_data;
std::string dot_output;
bool measure_temp;
std::vector<std::string> batch_per_network;
};
bool should_measure_pipeline_stats(const inference_runner_params& params);
CLI::App* create_run_command(CLI::App& parent, inference_runner_params& params);
hailo_status run_command(const inference_runner_params &params);
Expected<NetworkGroupInferResult> run_command_hef(const inference_runner_params &params);
std::string format_type_to_string(hailo_format_type_t format_type);
class RunCommand : public Command {
public:
explicit RunCommand(CLI::App &parent_app);
hailo_status execute() override;
private:
inference_runner_params m_params;
};
class InputNameToFilePairValidator : public CLI::Validator {
public:
InputNameToFilePairValidator() : CLI::Validator("InputNameToFilePair"), m_first_run(true), m_must_fail(false) {
func_ = [this](std::string &key_value_pair_str) {
bool old_first_run = m_first_run;
m_first_run = false;
if (m_must_fail) { // If a previous argument was not in key-value pair form, there shouldn't be more parameters
return std::string("Parse failed at (" + key_value_pair_str + ")");
}
size_t first_delimiter = key_value_pair_str.find("=");
if((std::string::npos == first_delimiter) || (key_value_pair_str.size() == first_delimiter + 1)) {
if (old_first_run) { // We only accept non-key-value pair form if it's the very first parameter
m_must_fail = true;
return CLI::ExistingFile(key_value_pair_str);
}
return std::string("Failed parsing key-value pair: (" + key_value_pair_str + ")");
}
auto file_path = key_value_pair_str.substr(first_delimiter + 1);
return CLI::ExistingFile(file_path);
};
desc_function_ = []() {
return "\t\tInput file path/paths. On single input network, give the full path of the data file.\n\
\t\tOn multiple inputs network, the format is input_name1=path1 input_name2=path2, where\n\
\t\tinput_name1 is the name of the input stream. If not given, random data will be used";
};
}
private:
bool m_first_run;
bool m_must_fail;
};
const static InputNameToFilePairValidator InputNameToFileMap;
class NetworkBatchValidator : public CLI::Validator {
public:
NetworkBatchValidator() : CLI::Validator(), m_must_fail(false) {
func_ = [this](std::string &key_value_pair_str) {
if (m_must_fail) { // If a previous argument was not in a valid key-value pair form, there shouldn't be more parameters
return std::string("Parse failed at (" + key_value_pair_str + ")");
}
size_t first_delimiter = key_value_pair_str.find("=");
if((std::string::npos == first_delimiter) || (key_value_pair_str.size() == first_delimiter + 1)) {
// We do not accept non-key-value pair form
m_must_fail = true;
return std::string("Failed parsing key-value pair: (" + key_value_pair_str + ")");
}
auto batch_size = key_value_pair_str.substr(first_delimiter + 1);
auto network_name = key_value_pair_str.substr(0, first_delimiter);
// Batch size must be a positive number
if (!is_positive_number(batch_size)) {
m_must_fail = true;
return std::string("Failed parsing batch size (" + batch_size + ") for network (" + network_name + "). batch should be a positive number.");
}
return std::string("");
};
}
private:
bool is_positive_number(const std::string &s)
{
bool is_number = (!s.empty()) && (std::all_of(s.begin(), s.end(), ::isdigit));
return is_number && (0 < std::stoi(s));
}
bool m_must_fail;
};
const static NetworkBatchValidator NetworkBatchMap;
#endif /* _HAILO_RUN_COMMAND_HPP_ */

View File

@@ -0,0 +1,154 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file scan_command.cpp
* @brief Scan hailo devices
**/
#include "scan_command.hpp"
#include "hailortcli.hpp"
#include "common/socket.hpp"
#include <iostream>
ScanSubcommand::ScanSubcommand(CLI::App &parent_app) :
Command(parent_app.add_subcommand("scan", "Shows all available devices")),
m_device_type(Device::Type::PCIE)
{
// TODO: `--target` and `udp` Device::TYPE::ETH are for backwards compatibility with the python implemention (`hailo`)
// TODO: Remove them (HRT-2676)
const HailoCheckedTransformer<Device::Type> device_type_transformer({
{ "pcie", Device::Type::PCIE },
{ "eth", Device::Type::ETH },
{ "udp", Device::Type::ETH },
});
auto *device_type_option = m_app->add_option("-d,--device-type,--target", m_device_type,
"Device type to use\n"
"Note: 'udp' is an alias for 'eth'.")
->transform(device_type_transformer)
->default_val("pcie");
// Ethernet options
auto *eth_options_group = m_app->add_option_group("Ethernet Device Options");
// TODO: `--ip` is for backwards compatibility with the python implemention (`hailo`)
// TODO: Remove it (HRT-2676)
auto *interface_ip_option = eth_options_group->add_option("--interface-ip", m_interface_ip_addr,
"Interface IP address to scan")
->default_val("")
->check(CLI::ValidIPV4);
auto *ip_option = eth_options_group->add_option("--ip", m_interface_ip_addr)
->default_val("")
->excludes(interface_ip_option)
->check(CLI::ValidIPV4);
auto *interface_name_option = eth_options_group->add_option("--interface", m_interface_name, "Interface name to scan")
->default_val("")
->excludes(interface_ip_option)
->excludes(ip_option);
m_app->parse_complete_callback([this, device_type_option, interface_ip_option, ip_option, interface_name_option]() {
bool eth_options_given = !interface_ip_option->empty() || !ip_option->empty() || !interface_name_option->empty();
// The user didn't put target, we can figure it ourself
if (device_type_option->empty()) {
if (eth_options_given) {
// User gave IP, target is eth
m_device_type = Device::Type::ETH;
}
else {
// Default is pcie
m_device_type = Device::Type::PCIE;
}
}
if (!eth_options_given && (m_device_type == Device::Type::ETH)) {
throw CLI::ParseError("Ethernet options not set", CLI::ExitCodes::InvalidError);
}
if (eth_options_given && (m_device_type != Device::Type::ETH)) {
throw CLI::ParseError("Ethernet options set on non eth device", CLI::ExitCodes::InvalidError);
}
});
std::vector<DeprecationActionPtr> actions{
std::make_shared<ValueDeprecation>(device_type_option, "udp", "eth"),
std::make_shared<OptionDeprecation>(ip_option, "--interface-ip")
};
hailo_deprecate_options(m_app, actions, false);
}
hailo_status ScanSubcommand::execute()
{
switch (m_device_type)
{
case Device::Type::PCIE:
return scan_pcie();
case Device::Type::ETH:
return scan_ethernet(m_interface_ip_addr, m_interface_name).status();
default:
std::cerr << "Unkown target" << std::endl;
return HAILO_INVALID_ARGUMENT;
}
}
hailo_status ScanSubcommand::scan_pcie()
{
auto scan_result = Device::scan_pcie();
CHECK_SUCCESS(scan_result.status(), "Error scan failed status = {}", scan_result.status());
if (scan_result->size() == 0) {
std::cout << "Hailo PCIe devices not found" << std::endl;
}
else {
std::cout << "Hailo PCIe Devices:" << std::endl;
for (const auto& device_info : scan_result.value()) {
auto device_info_str = Device::pcie_device_info_to_string(device_info);
CHECK_EXPECTED_AS_STATUS(device_info_str);
std::cout << "[-] Device BDF: " << device_info_str.value() << std::endl;
}
}
return HAILO_SUCCESS;
}
Expected<std::vector<std::string>> ScanSubcommand::scan_ethernet(const std::string &interface_ip_addr,
const std::string &interface_name)
{
const std::chrono::seconds timeout(3);
std::vector<hailo_eth_device_info_t> device_infos;
if (!interface_ip_addr.empty()) {
auto result = Device::scan_eth_by_host_address(interface_ip_addr, timeout);
if (!result) {
std::cerr << "Failed scanning ethernet device from host address (" << result.status() << ")" << std::endl;
return make_unexpected(result.status());
}
device_infos = result.release();
} else {
auto result = Device::scan_eth(interface_name, timeout);
if (!result) {
std::cerr << "Failed scanning ethernet device from interface name (" << result.status() << ")" << std::endl;
return make_unexpected(result.status());
}
device_infos = result.release();
}
std::cout << "Hailo Ethernet Devices:" << std::endl;
std::vector<std::string> ip_addresses;
ip_addresses.reserve(device_infos.size());
char textual_ip_address[INET_ADDRSTRLEN] = {};
for (size_t i = 0; i < device_infos.size(); i++) {
auto status = Socket::ntop(AF_INET, &(device_infos[i].device_address.sin_addr), textual_ip_address,
INET_ADDRSTRLEN);
if (status != HAILO_SUCCESS) {
std::cerr << "Could not convert ip address to textual format (inet_ntop has failed)" << std::endl;
continue;
}
std::cout << "[-] Board IP: " << textual_ip_address << std::endl;
ip_addresses.emplace_back(textual_ip_address);
}
return ip_addresses;
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file scan_command.hpp
* @brief Scan hailo devices
**/
#ifndef _HAILO_SCAN_COMMAND_HPP_
#define _HAILO_SCAN_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "CLI/CLI.hpp"
class ScanSubcommand final : public Command {
public:
explicit ScanSubcommand(CLI::App &parent_app);
hailo_status execute() override;
static Expected<std::vector<std::string>> scan_ethernet(const std::string &interface_ip_addr,
const std::string &interface_name);
private:
hailo_status scan_pcie();
Device::Type m_device_type;
// Ethernet scan options
std::string m_interface_ip_addr;
std::string m_interface_name;
};
#endif /* _HAILO_SCAN_COMMAND_HPP_ */

View File

@@ -0,0 +1,209 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file sensor_config_command.cpp
* @brief Config sensor attached to the Hailo chip
**/
#include "sensor_config_command.hpp"
SensorConfigCommand::SensorConfigCommand(CLI::App &parent_app) :
ContainerCommand(parent_app.add_subcommand("sensor-config", "Config sensor attached to the Hailo chip"))
{
add_subcommand<SensorStoreConfigSubcommand>();
add_subcommand<SensorLoadConfigSubcommand>();
add_subcommand<SensorResetSubcommand>();
add_subcommand<SensorSectionsInfoSubcommand>();
add_subcommand<SensorDumpConfigSubcommand>();
add_subcommand<SensorStoreISPConfigSubcommand>();
add_subcommand<SensorSetGenericSlaveSubcommand>();
}
Expected<std::string> convert_sensor_type_to_string(uint32_t sensor_type)
{
switch (sensor_type) {
case HAILO_SENSOR_TYPES_GENERIC:
return std::string("SENSOR_GENERIC");
case HAILO_SENSOR_TYPES_ONSEMI_AR0220AT:
return std::string("ONSEMI_AR0220AT");
case HAILO_SENSOR_TYPES_RASPICAM:
return std::string("SENSOR_RASPICAM");
case HAILO_SENSOR_TYPES_ONSEMI_AS0149AT:
return std::string("ONSEMI_AS0149AT");
case HAILO_SENSOR_TYPES_HAILO8_ISP:
return std::string("HAILO8_ISP");
default:
LOGGER__ERROR("Failed converting sensor type to string");
return make_unexpected(HAILO_NOT_FOUND);
}
}
SensorStoreConfigSubcommand::SensorStoreConfigSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("store-sensor-config", "Store a sensor configuration to a Hailo device")), m_store_config_params()
{
m_app->add_option("section_index", m_store_config_params.section_index, "Sensor index")
->required();
m_app->add_option("config_file_path", m_config_file_path, "Config file path (csv)")
->check(CLI::ExistingFile)
->required();
m_app->add_option("sensor_type", m_store_config_params.sensor_type, "Type of sensor")
->transform(HailoCheckedTransformer<hailo_sensor_types_t>({
{ "SENSOR_GENERIC", HAILO_SENSOR_TYPES_GENERIC },
{ "ONSEMI_AR0220AT", HAILO_SENSOR_TYPES_ONSEMI_AR0220AT },
{ "SENSOR_RASPICAM", HAILO_SENSOR_TYPES_RASPICAM },
{ "ONSEMI_AS0149AT", HAILO_SENSOR_TYPES_ONSEMI_AS0149AT },
{ "HAILO8_ISP", HAILO_SENSOR_TYPES_HAILO8_ISP }
}))
->required();
m_app->add_option("--reset-config-size", m_store_config_params.reset_config_size, "The size of the reset configuration data");
m_app->add_option("--config-height", m_store_config_params.config_height, "Configuration resolution height");
m_app->add_option("--config-width", m_store_config_params.config_width, "Configuration resolution width");
m_app->add_option("--config-fps", m_store_config_params.config_fps, "Configuration resolution fps");
m_app->add_option("--config-name", m_store_config_params.config_name, "Configuration name")
->default_val("UNINITIALIZED");
}
hailo_status SensorStoreConfigSubcommand::execute_on_device(Device &device)
{
return device.store_sensor_config(m_store_config_params.section_index, m_store_config_params.sensor_type,
m_store_config_params.reset_config_size, m_store_config_params.config_height,
m_store_config_params.config_width, m_store_config_params.config_fps, m_config_file_path,
m_store_config_params.config_name);
}
SensorLoadConfigSubcommand::SensorLoadConfigSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("load-config", "Load the sensor configuration stored in the given section"))
{
m_app->add_option("section_index", m_section_index, "Sensor index")
->required();
}
hailo_status SensorLoadConfigSubcommand::execute_on_device(Device &device)
{
return device.sensor_load_and_start_config(m_section_index);
}
SensorResetSubcommand::SensorResetSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("reset-sensor", "Load reset configuration stored in the given section"))
{
m_app->add_option("section_index", m_section_index, "Sensor index")
->required();
}
hailo_status SensorResetSubcommand::execute_on_device(Device &device)
{
return device.sensor_reset(m_section_index);
}
SensorSectionsInfoSubcommand::SensorSectionsInfoSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("get-sections-info", "Get the flash sections information"))
{}
hailo_status SensorSectionsInfoSubcommand::execute_on_device(Device &device)
{
auto sections_info = device.sensor_get_sections_info();
CHECK_EXPECTED_AS_STATUS(sections_info);
return print_sections_info((SENSOR_CONFIG__section_info_t*)sections_info->data());
}
hailo_status SensorSectionsInfoSubcommand::print_sections_info(SENSOR_CONFIG__section_info_t *operation_cfg)
{
for (uint32_t section_index = 0; section_index < SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT; section_index++) {
SENSOR_CONFIG__section_info_t *section_info = &operation_cfg[section_index];
std::cout << "======== section_index: " << section_index << " =========" << std::endl;
if (section_info->is_free) {
std::cout << "Section is not active\n" << std::endl;
}
else {
std::string reset_config = section_info->no_reset_offset ? "not valid" : "valid";
auto sensor_type_expected = convert_sensor_type_to_string(section_info->sensor_type);
CHECK_EXPECTED_AS_STATUS(sensor_type_expected, "Failed convert sensor type to string");
std::cout << "Configuration Name: " << section_info->config_name << "\n";
std::cout << "Sensor Type: " << sensor_type_expected.value() << "\n";
std::cout << "Configuration lines number: " << (section_info->config_size / sizeof(SENSOR_CONFIG__operation_cfg_t)) << "\n";
std::cout << "Configuration size in bytes: " << section_info->config_size << "\n";
std::cout << "Reset configuration: " << reset_config << "\n";
std::cout << "Reset configuration lines number: " << section_info->reset_config_size << "\n";
std::cout << "Configuration resolution: [height " << section_info->config_height << " : width " << section_info->config_width << "]" << "\n";
std::cout << "Configuration fps: " << section_info->config_fps << "\n";
std::cout << "Section configuration version: " << section_info->section_version << "\n";
std::cout << "Section is active\n" << std::endl;
}
}
return HAILO_SUCCESS;
}
// TODO: change "get-config" to "dump-config" after solving backward compatibility issues
SensorDumpConfigSubcommand::SensorDumpConfigSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("get-config", "Dumps the configuration stored in the given section into a csv file"))
{
m_app->add_option("section_index", m_section_index, "Sensor index")
->check(CLI::Range(0, (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - 1)))
->required();
m_app->add_option("config_file_path", m_output_file_path, "File path to write section configuration")
->required();
}
hailo_status SensorDumpConfigSubcommand::execute_on_device(Device &device)
{
return device.sensor_dump_config(m_section_index, m_output_file_path);
}
SensorStoreISPConfigSubcommand::SensorStoreISPConfigSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("store_isp_config", "Store an ISP configuration to Hailo device, in section index " + std::to_string(SENSOR_CONFIG__ISP_SECTION_INDEX))),
m_store_config_params()
{
m_app->add_option("isp_static_config_file_path", m_isp_static_config_file_path, "ISP static config file path")
->required();
m_app->add_option("isp_runtime_config_file_path", m_isp_runtime_config_file_path, "ISP runtime config file path")
->required();
m_app->add_option("--reset-config-size", m_store_config_params.reset_config_size, "The size of the reset configuration data");
m_app->add_option("--config-height", m_store_config_params.config_height, "Configuration resolution height");
m_app->add_option("--config-width", m_store_config_params.config_width, "Configuration resolution width");
m_app->add_option("--config-fps", m_store_config_params.config_fps, "Configuration resolution fps");
m_app->add_option("--config-name", m_store_config_params.config_name, "Configuration name")
->default_val("UNINITIALIZED");
}
hailo_status SensorStoreISPConfigSubcommand::execute_on_device(Device &device)
{
return device.store_isp_config(m_store_config_params.reset_config_size, m_store_config_params.config_height,
m_store_config_params.config_width, m_store_config_params.config_fps, m_isp_static_config_file_path,
m_isp_runtime_config_file_path, m_store_config_params.config_name);
}
SensorSetGenericSlaveSubcommand::SensorSetGenericSlaveSubcommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("set_generic_slave", "Set a custom i2c slave that can be used"))
{
m_app->add_option("slave_address", m_sensor_i2c_slave_info.slave_address, "The address of the i2c slave")
->required();
m_app->add_option("register_address_size", m_sensor_i2c_slave_info.register_address_size, "Slave offset length in bytes")
->required();
m_app->add_option("bus_index", m_sensor_i2c_slave_info.bus_index, "The bus number the i2c slave is connected to")
->required();
m_app->add_option("--should-hold-bus", m_sensor_i2c_slave_info.should_hold_bus, "Should hold the bus during the read")
->default_val("false");
m_app->add_option("--slave-endianness", m_sensor_i2c_slave_info.endianness)
->transform(HailoCheckedTransformer<i2c_slave_endianness_t>({
{ "BIG_ENDIAN", I2C_SLAVE_ENDIANNESS_BIG_ENDIAN },
{ "LITTLE_ENDIAN", I2C_SLAVE_ENDIANNESS_LITTLE_ENDIAN }
}))
->default_val(I2C_SLAVE_ENDIANNESS_BIG_ENDIAN);
}
hailo_status SensorSetGenericSlaveSubcommand::execute_on_device(Device &device)
{
return device.sensor_set_generic_i2c_slave(m_sensor_i2c_slave_info.slave_address, m_sensor_i2c_slave_info.register_address_size,
m_sensor_i2c_slave_info.bus_index, m_sensor_i2c_slave_info.should_hold_bus, m_sensor_i2c_slave_info.endianness);
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file sensor_config_command.hpp
* @brief Config sensor attached to the Hailo chip
**/
#ifndef _HAILO_SENSOR_CONFIG_COMMAND_HPP_
#define _HAILO_SENSOR_CONFIG_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "sensor_config_exports.h"
#include "CLI/CLI.hpp"
struct store_config_params_t {
uint32_t section_index;
hailo_sensor_types_t sensor_type;
uint32_t reset_config_size;
uint16_t config_height;
uint16_t config_width;
uint16_t config_fps;
std::string config_name;
};
class SensorStoreConfigSubcommand final : public DeviceCommand {
public:
explicit SensorStoreConfigSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
store_config_params_t m_store_config_params;
std::string m_config_file_path;
};
class SensorLoadConfigSubcommand final : public DeviceCommand {
public:
explicit SensorLoadConfigSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
uint8_t m_section_index;
};
class SensorResetSubcommand final : public DeviceCommand {
public:
explicit SensorResetSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
uint8_t m_section_index;
};
class SensorSectionsInfoSubcommand final : public DeviceCommand {
public:
explicit SensorSectionsInfoSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
static hailo_status print_sections_info(SENSOR_CONFIG__section_info_t *operation_cfg);
};
class SensorDumpConfigSubcommand final : public DeviceCommand {
public:
explicit SensorDumpConfigSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
uint8_t m_section_index;
std::string m_output_file_path;
};
class SensorStoreISPConfigSubcommand final : public DeviceCommand {
public:
explicit SensorStoreISPConfigSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
store_config_params_t m_store_config_params;
std::string m_isp_static_config_file_path;
std::string m_isp_runtime_config_file_path;
};
class SensorSetGenericSlaveSubcommand final : public DeviceCommand {
public:
explicit SensorSetGenericSlaveSubcommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
SENSOR_I2C_SLAVE_INFO_t m_sensor_i2c_slave_info;
};
class SensorConfigCommand final : public ContainerCommand {
public:
explicit SensorConfigCommand(CLI::App &parent_app);
};
#endif /* _HAILO_SENSOR_CONFIG_COMMAND_HPP_ */

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file ssb_update_command.cpp
* @brief Update second stage boot on hailo device with flash
**/
#include "ssb_update_command.hpp"
#include "common/file_utils.hpp"
SSBUpdateCommand::SSBUpdateCommand(CLI::App &parent_app) :
DeviceCommand(parent_app.add_subcommand("ssb-update", "Second stage boot update command (only for flash based devices)")),
m_second_stage_path()
{
m_app->add_option("file_path", m_second_stage_path, "The path to the second stage boot binary")
->required()
->check(CLI::ExistingFile);
}
hailo_status SSBUpdateCommand::execute_on_device(Device &device)
{
auto second_stage = read_binary_file(m_second_stage_path);
if (!second_stage) {
std::cerr << "Failed reading second stage boot file " << second_stage.status() << std::endl;
return second_stage.status();
}
std::cout << "Updating second stage boot..." << std::endl;
auto status = device.second_stage_update(second_stage->data(), static_cast<uint32_t>(second_stage->size()));
if (HAILO_SUCCESS != status) {
std::cerr << "Update second stage boot failed with error: " << status << std::endl;
return status;
}
std::cout << "second stage boot has been updated successfully" << std::endl;
return HAILO_SUCCESS;
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
* Distributed under the MIT license (https://opensource.org/licenses/MIT)
**/
/**
* @file ssb_update_command.hpp
* @brief Update second stage boot on hailo device with flash
**/
#ifndef _HAILORTCLI_SSB_UPDATE_COMMAND_HPP_
#define _HAILORTCLI_SSB_UPDATE_COMMAND_HPP_
#include "hailortcli.hpp"
#include "command.hpp"
#include "hailo/hailort.h"
#include "hailo/device.hpp"
#include "hailo/buffer.hpp"
#include "CLI/CLI.hpp"
class SSBUpdateCommand : public DeviceCommand {
public:
explicit SSBUpdateCommand(CLI::App &parent_app);
protected:
virtual hailo_status execute_on_device(Device &device) override;
private:
std::string m_second_stage_path;
};
#endif /* _HAILORTCLI_SSB_UPDATE_COMMAND_HPP_ */

Some files were not shown because too many files have changed in this diff Show More