codegen_vhls.cc 4.68 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12 13 14 15 16 17 18 19
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

20 21 22 23 24
/*!
 * \file codegen_vhls.cc
 */
#include <vector>
#include <string>
25
#include "codegen_vhls.h"
26 27
#include "../build_common.h"
#include "../../runtime/opencl/sdaccel/sdaccel_module.h"
28 29 30 31 32 33 34 35

namespace tvm {
namespace codegen {

void CodeGenVivadoHLS::Init(bool output_ssa) {
  CodeGenC::Init(output_ssa);

  this->stream << "#include <ap_int.h>\n\n";
36
  this->stream << "#include <algorithm>\n\n";
37 38
}

39
void CodeGenVivadoHLS::PrintType(DataType t, std::ostream& os) {
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 71 72 73 74 75 76 77 78 79
  if (t.is_uint()) {
    switch (t.bits()) {
      case 8:
        os << "unsigned char"; break;
      case 16:
        os << "unsigned short"; break;
      case 32:
        os << "unsigned int"; break;
      case 64:
        os << "unsigned long long"; break;
      default:
        os << "ap_uint<" << t.bits() << ">"; break;
    }
  } else if (t.is_int()) {
    switch (t.bits()) {
      case 8:
        os << "char"; break;
      case 16:
        os << "short"; break;
      case 32:
        os << "int"; break;
      case 64:
        os << "long long"; break;
      default:
        os << "ap_int<" << t.bits() << ">"; break;
    }
  } else {
    CodeGenC::PrintType(t, os);
  }
}

void CodeGenVivadoHLS::AddFunction(LoweredFunc f) {
  this->stream << "extern \"C\" ";
  CodeGenC::AddFunction(f);
}

void CodeGenVivadoHLS::PreFunctionBody(LoweredFunc f) {
  for (size_t i = 0; i < f->args.size(); ++i) {
    Var v = f->args[i];
    std::string vid = GetVarID(v.get());
80
    if (v.dtype().is_handle()) {
81 82 83 84 85 86 87
      this->stream << "#pragma HLS INTERFACE m_axi port=" << vid << "  offset=slave bundle=gmem\n";
    }
    this->stream << "#pragma HLS INTERFACE s_axilite port=" << vid << " bundle=control\n";
  }
  this->stream << "#pragma HLS INTERFACE s_axilite port=return bundle=control\n\n";
}

88 89 90 91 92 93 94 95 96 97 98 99
template<typename T>
inline void PrintBinaryExpr(const T* op,
                            const char *opstr,
                            std::ostream& os,  // NOLINT(*)
                            CodeGenVivadoHLS* p) {
  os << opstr << '(';
  p->PrintExpr(op->a, os);
  os << ", ";
  p->PrintExpr(op->b, os);
  os << ')';
}

100
void CodeGenVivadoHLS::VisitExpr_(const MinNode *op, std::ostream& os) {  // NOLINT(*)
101
  const char *opstr = "std::min";
102 103
  if (op->dtype.is_float()) {
    switch (op->dtype.bits()) {
104 105 106 107 108 109 110 111 112 113
      case 32:
        opstr = "fminf"; break;
      case 64:
        opstr = "fmin"; break;
    }
  }

  PrintBinaryExpr(op, opstr, os, this);
}

114
void CodeGenVivadoHLS::VisitExpr_(const MaxNode *op, std::ostream& os) {  // NOLINT(*)
115
  const char *opstr = "std::max";
116 117
  if (op->dtype.is_float()) {
    switch (op->dtype.bits()) {
118 119 120 121 122 123 124 125 126 127
      case 32:
        opstr = "fmaxf"; break;
      case 64:
        opstr = "fmax"; break;
    }
  }

  PrintBinaryExpr(op, opstr, os, this);
}

128

129
runtime::Module BuildSDAccel(Array<LoweredFunc> funcs, std::string target_str) {
130 131 132 133
  using tvm::runtime::Registry;
  bool output_ssa = false;
  CodeGenVivadoHLS cg;

134
  // Generate source code for get_source().
135 136 137 138
  cg.Init(output_ssa);
  for (LoweredFunc f : funcs) {
    cg.AddFunction(f);
  }
139 140 141
  std::string whole_code = cg.Finish();

  // Generate source code for compilation.
142
  Array<Array<PrimExpr> > kernel_info;
143 144 145 146 147 148 149 150
  for (LoweredFunc f : funcs) {
    CodeGenVivadoHLS cg;
    cg.Init(output_ssa);
    cg.AddFunction(f);
    std::string code = cg.Finish();
    if (const auto* f = runtime::Registry::Get("tvm_callback_vhls_postproc")) {
      code = (*f)(code).operator std::string();
    }
151
    kernel_info.push_back(Array<PrimExpr>({f->name, code}));
152 153 154 155
  }

  std::string xclbin;
  if (const auto* f = Registry::Get("tvm_callback_sdaccel_compile")) {
156
    Target target = Target::Create(target_str);
157
    xclbin = (*f)(kernel_info, target->device_name).operator std::string();
158 159 160
  } else {
    LOG(FATAL) << "Cannot compile Vivado HLS code.";
  }
161
  return SDAccelModuleCreate(xclbin, "xclbin", ExtractFuncInfo(funcs), whole_code);
162 163
}

164
TVM_REGISTER_GLOBAL("codegen.build_sdaccel")
165
.set_body_typed(BuildSDAccel);
166 167 168

}  // namespace codegen
}  // namespace tvm