codegen_vhls.cc 5.43 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
  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);
  }
}

71 72
void CodeGenVivadoHLS::PrintFuncPrefix() {
  stream << "extern \"C\" void";
73 74
}

75 76 77
void CodeGenVivadoHLS::PreFunctionBody(const PrimFunc& f) {
  for (size_t i = 0; i < f->params.size(); ++i) {
    Var v = f->params[i];
78
    std::string vid = GetVarID(v.get());
79
    if (v.dtype().is_handle()) {
80 81 82 83 84 85 86
      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";
}

87 88 89 90 91 92 93 94 95 96 97 98
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 << ')';
}

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

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

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

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

127

128
runtime::Module BuildSDAccel(IRModule mod, std::string target_str) {
129 130 131 132
  using tvm::runtime::Registry;
  bool output_ssa = false;
  CodeGenVivadoHLS cg;

133
  // Generate source code for get_source().
134
  cg.Init(output_ssa);
135 136 137 138 139 140 141 142 143

  for (auto kv :  mod->functions) {
    CHECK(kv.second->IsInstance<PrimFuncNode>())
        << "CodeGenVHLS: Can only take PrimFunc";
    auto f = Downcast<PrimFunc>(kv.second);
    auto calling_conv = f->GetAttr<Integer>(tvm::attr::kCallingConv);
    CHECK(calling_conv.defined() &&
          calling_conv->value == static_cast<int>(CallingConv::kDeviceKernelLaunch))
        << "CodeGenVLHS: expect calling_conv equals CallingConv::kDeviceKernelLaunch";
144 145
    cg.AddFunction(f);
  }
146

147 148 149
  std::string whole_code = cg.Finish();

  // Generate source code for compilation.
150
  Array<Array<PrimExpr> > kernel_info;
151 152 153 154 155

  for (auto kv :  mod->functions) {
    CHECK(kv.second->IsInstance<PrimFuncNode>())
        << "CodeGenOpenCL: Can only take PrimFunc";
    auto f = Downcast<PrimFunc>(kv.second);
156 157 158 159 160 161 162
    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();
    }
163 164 165 166 167 168

    auto global_symbol = f->GetAttr<runtime::String>(tvm::attr::kGlobalSymbol);
    CHECK(global_symbol.defined())
        << "CodeGenC: Expect PrimFunc to have the global_symbol attribute";
    std::string func_name = global_symbol;
    kernel_info.push_back(Array<PrimExpr>({func_name, code}));
169 170 171 172
  }

  std::string xclbin;
  if (const auto* f = Registry::Get("tvm_callback_sdaccel_compile")) {
173
    Target target = Target::Create(target_str);
174
    xclbin = (*f)(kernel_info, target->device_name).operator std::string();
175 176 177
  } else {
    LOG(FATAL) << "Cannot compile Vivado HLS code.";
  }
178
  return SDAccelModuleCreate(xclbin, "xclbin", ExtractFuncInfo(mod), whole_code);
179 180
}

181
TVM_REGISTER_GLOBAL("target.build.sdaccel")
182
.set_body_typed(BuildSDAccel);
183 184 185

}  // namespace codegen
}  // namespace tvm