diff --git a/xls/contrib/xlscc/translate_block.cc b/xls/contrib/xlscc/translate_block.cc index 057ec0b00c..43dab8ed16 100644 --- a/xls/contrib/xlscc/translate_block.cc +++ b/xls/contrib/xlscc/translate_block.cc @@ -308,6 +308,7 @@ absl::StatusOr Translator::GenerateIR_Block( xls::Package* package, const HLSBlock& block, int top_level_init_interval, const ChannelOptions& channel_options) { package_ = package; + channel_options_ = channel_options; absl::flat_hash_map channels_by_name; for (const HLSChannel& channel : block.channels()) { @@ -334,8 +335,7 @@ absl::StatusOr Translator::GenerateIR_Block( const xls::SourceInfo body_loc = GetLoc(*definition); std::list top_decls; - absl::flat_hash_map - unused_strictness_options = channel_options.strictness_map; + unused_strictness_options_ = channel_options.strictness_map; for (int pidx = 0; pidx < definition->getNumParams(); ++pidx) { const clang::ParmVarDecl* param = definition->getParamDecl(pidx); @@ -355,7 +355,7 @@ absl::StatusOr Translator::GenerateIR_Block( ExternalChannelInfo channel_info = {.decl = param}; XLS_ASSIGN_OR_RETURN(channel_info.strictness, GetChannelStrictness(*param, channel_options, - unused_strictness_options)); + unused_strictness_options_)); if (channel_spec.type() == ChannelType::CHANNEL_TYPE_DIRECT_IN) { channel_info.interface_type = InterfaceType::kDirect; XLS_ASSIGN_OR_RETURN(StrippedType stripped, @@ -397,17 +397,21 @@ absl::StatusOr Translator::GenerateIR_Block( top_decls.push_back(channel_info); } - if (!unused_strictness_options.empty()) { + + auto ret = GenerateIR_Block(package, block, /*this_type=*/nullptr, + /*this_decl=*/nullptr, top_decls, body_loc, + top_level_init_interval, + /*force_static=*/true, + /*member_references_become_channels=*/false); + + // Check including local channels? + if (!unused_strictness_options_.empty()) { return absl::InvalidArgumentError( absl::StrFormat("Unused channel strictness options: %s", - ToString(unused_strictness_options))); + ToString(unused_strictness_options_))); } - return GenerateIR_Block(package, block, /*this_type=*/nullptr, - /*this_decl=*/nullptr, top_decls, body_loc, - top_level_init_interval, - /*force_static=*/true, - /*member_references_become_channels=*/false); + return ret; } absl::StatusOr Translator::GenerateIR_Block( @@ -691,6 +695,7 @@ absl::StatusOr Translator::GenerateIR_BlockFromClass( xls::Package* package, HLSBlock* block_spec_out, int top_level_init_interval, const ChannelOptions& channel_options) { package_ = package; + channel_options_ = channel_options; block_spec_out->Clear(); // Create external channels @@ -1936,11 +1941,6 @@ absl::Status Translator::GenerateIRBlockCheck( channel_names_in_block.insert(channel.name()); } - if (top_decls.size() != block.channels_size()) { - return absl::InvalidArgumentError(absl::StrFormat( - "Top function has %i parameters, but block proto defines %i channels", - top_decls.size(), block.channels_size())); - } for (const ExternalChannelInfo& top_decl : top_decls) { const clang::NamedDecl* decl = top_decl.decl; @@ -1952,12 +1952,6 @@ absl::Status Translator::GenerateIRBlockCheck( channel_names_in_block.erase(decl->getNameAsString()); } - if (!channel_names_in_block.empty()) { - return absl::InvalidArgumentError(absl::StrFormat( - "Block proto contains %i channels not in function prototype", - channel_names_in_block.size())); - } - return absl::OkStatus(); } diff --git a/xls/contrib/xlscc/translate_io.cc b/xls/contrib/xlscc/translate_io.cc index fe49ad83ea..009a21a53d 100644 --- a/xls/contrib/xlscc/translate_io.cc +++ b/xls/contrib/xlscc/translate_io.cc @@ -24,6 +24,8 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_format.h" +#include "clang/include/clang/AST/Attr.h" +#include "clang/include/clang/AST/Decl.h" #include "clang/include/clang/AST/DeclTemplate.h" #include "clang/include/clang/AST/Expr.h" #include "clang/include/clang/AST/ExprCXX.h" @@ -338,6 +340,20 @@ absl::StatusOr Translator::TypeIsChannel(clang::QualType param, return TypeIsChannel(subst->getReplacementType(), loc); } + if (auto paren = clang::dyn_cast(type)) { + return TypeIsChannel(paren->desugar(), loc); + } + + // NOTE: TranslateTypeFromClang() may also need updating when changing this + if (auto attributed = clang::dyn_cast(type)) { + auto type_attr = + clang::dyn_cast(attributed->getAttr()); + std::string annotation = type_attr->getAnnotation().str(); + if (annotation == "hls_memory") { + return true; + } + } + if (type->getTypeClass() == clang::Type::TypeClass::TemplateSpecialization && type->isRecordType()) { // Up-cast to avoid multiple inheritance of getAsRecordDecl() @@ -404,6 +420,11 @@ absl::StatusOr> Translator::GetChannelType( return GetChannelType(subst->getReplacementType(), ctx, loc); } + if (auto paren = + clang::dyn_cast(stripped.base.getTypePtr())) { + return GetChannelType(paren->desugar(), ctx, loc); + } + if (auto template_spec = clang::dyn_cast( stripped.base.getTypePtr()); @@ -434,6 +455,38 @@ absl::StatusOr> Translator::GetChannelType( return absl::UnimplementedError(ErrorMessage( loc, "Channel RecordDecl should be ClassTemplateSpecializationDecl")); } + } else if (auto attributed = clang::dyn_cast( + stripped.base.getTypePtr()); + attributed != nullptr) { + auto type_attr = + clang::dyn_cast(attributed->getAttr()); + XLSCC_CHECK_NE(type_attr, nullptr, loc); + + std::string annotation = type_attr->getAnnotation().str(); + std::shared_ptr ret; + + // NOTE: TypeIsChannel() may also need updating when changing this + + if (annotation == "hls_array_as_tuple") { + XLS_ASSIGN_OR_RETURN(ret, + TranslateTypeFromClang(attributed->desugar(), loc, + /*array_as_tuple=*/true)); + } else if (annotation == "hls_memory") { + XLS_ASSIGN_OR_RETURN(std::shared_ptr obj_type, + TranslateTypeFromClang(attributed->desugar(), loc)); + if (obj_type->Is()) { + auto array_type = std::dynamic_pointer_cast(obj_type); + return std::make_shared(array_type->GetElementType(), + array_type->GetSize()); + } else { + return absl::UnimplementedError( + ErrorMessage(loc, "hls_memory not applicable to type: %s", + obj_type->debug_string().c_str())); + } + } else { + return absl::InvalidArgumentError(ErrorMessage( + loc, "Unknown type annotation for channel: %s", annotation)); + } } else { return absl::UnimplementedError(ErrorMessage( loc, @@ -490,6 +543,9 @@ absl::StatusOr Translator::InterceptIOOp( const clang::Expr* object = nullptr; std::string op_name; + // for memory by operator / subscript + CValue addr_val; + if (auto member_call = clang::dyn_cast(expr)) { object = member_call->getImplicitObjectArgument(); @@ -508,6 +564,27 @@ absl::StatusOr Translator::InterceptIOOp( } object = operator_call->getArg(0); op_name = "__memory_by_operator"; + } else if (auto array_subscript = + clang::dyn_cast(expr)) { + XLS_ASSIGN_OR_RETURN(CValue arr_val, + GenerateIR_Expr(array_subscript->getBase(), loc)); + + if (arr_val.lvalue() == nullptr || arr_val.lvalue()->is_null()) { + return no_op_return; + } + + if (!arr_val.lvalue()->is_channel()) { + return no_op_return; + } + if (arr_val.lvalue()->channel_leaf()->memory_size <= 0) { + return absl::InvalidArgumentError( + ErrorMessage(loc, "Array subscript on non-memory channel")); + } + + object = array_subscript->getBase(); + op_name = "__memory_by_subscript"; + XLS_ASSIGN_OR_RETURN(addr_val, + GenerateIR_Expr(array_subscript->getIdx(), loc)); } else { return no_op_return; } @@ -528,14 +605,23 @@ absl::StatusOr Translator::InterceptIOOp( return no_op_return; } - auto call = clang::dyn_cast(expr); std::vector arg_vals; - arg_vals.resize(call->getNumArgs()); - for (int64_t arg = 0; arg < call->getNumArgs(); ++arg) { - XLS_ASSIGN_OR_RETURN( - arg_vals[arg], - GenerateIR_Expr(call->getArg(static_cast(arg)), loc)); + const clang::CallExpr* call = nullptr; + + if (op_name != "__memory_by_subscript") { + call = clang::dyn_cast(expr); + arg_vals.resize(call->getNumArgs()); + + for (int64_t arg = 0; arg < call->getNumArgs(); ++arg) { + XLS_ASSIGN_OR_RETURN( + arg_vals[arg], + GenerateIR_Expr(call->getArg(static_cast(arg)), loc)); + } + } + + if (op_name == "__memory_by_operator") { + addr_val = arg_vals.at(1); } enum class TokenReturnModes { kNone, kDirect, kTuple }; @@ -717,10 +803,9 @@ absl::StatusOr Translator::InterceptIOOp( op.op = OpType::kWrite; op.is_blocking = true; } - } else if (op_name == "__memory_by_operator") { - CValue addr_val = arg_vals.at(1); - CHECK(addr_val.valid()); - + } else if (op_name == "__memory_by_operator" || + op_name == "__memory_by_subscript") { + XLSCC_CHECK(addr_val.valid(), loc); const bool is_write = assignment_value.valid(); op.is_blocking = true; diff --git a/xls/contrib/xlscc/translator.cc b/xls/contrib/xlscc/translator.cc index 44bffc6963..0d7780cfa2 100644 --- a/xls/contrib/xlscc/translator.cc +++ b/xls/contrib/xlscc/translator.cc @@ -593,6 +593,7 @@ absl::StatusOr Translator::GetArrayElement(const CValue& arr_val, TrackedBValue index_bval, const xls::SourceInfo& loc) { XLSCC_CHECK(index_bval.GetType()->IsBits(), loc); + XLSCC_CHECK(arr_val.type()->Is(), loc); auto arr_type = arr_val.type()->As(); TrackedBValue rval; @@ -2499,14 +2500,26 @@ absl::Status Translator::Assign(const clang::Expr* lvalue, const CValue& rvalue, XLS_RETURN_IF_ERROR(GenerateIR_Expr(lvalue, loc).status()); return absl::OkStatus(); } - if (auto* cast = clang::dyn_cast(lvalue)) { - XLS_ASSIGN_OR_RETURN(CValue idx_val, GenerateIR_Expr(cast->getIdx(), loc)); - XLS_ASSIGN_OR_RETURN(CValue arr_val, GenerateIR_Expr(cast->getBase(), loc)); + if (auto* array_subscript = + clang::dyn_cast(lvalue)) { + XLS_ASSIGN_OR_RETURN( + IOOpReturn ret, InterceptIOOp(array_subscript, GetLoc(*array_subscript), + /*assignment_value=*/rvalue)); + // If this call is an IO op, then return the IO value, rather than + // generating the call. + if (!ret.generate_expr) { + return absl::OkStatus(); + } + + XLS_ASSIGN_OR_RETURN(CValue idx_val, + GenerateIR_Expr(array_subscript->getIdx(), loc)); + XLS_ASSIGN_OR_RETURN(CValue arr_val, + GenerateIR_Expr(array_subscript->getBase(), loc)); XLS_ASSIGN_OR_RETURN( CValue arr_rvalue, UpdateArrayElement(arr_val, idx_val.rvalue(), rvalue, loc)); - return Assign(cast->getBase(), arr_rvalue, loc); + return Assign(array_subscript->getBase(), arr_rvalue, loc); } if (auto member_expr = clang::dyn_cast(lvalue)) { // Assign to a struct element @@ -5056,6 +5069,8 @@ absl::StatusOr Translator::HandleConstructors( absl::StatusOr Translator::GenerateIR_Expr(const clang::Expr* expr, const xls::SourceInfo& loc) { + XLSCC_CHECK_NE(expr, nullptr, loc); + // Intercept numeric constexprs XLS_ASSIGN_OR_RETURN(std::optional numeric_constexpr, EvaluateNumericConstExpr(expr, loc)); @@ -5227,17 +5242,28 @@ absl::StatusOr Translator::GenerateIR_Expr(const clang::Expr* expr, return absl::UnimplementedError(ErrorMessage( loc, "Unsupported constructor argument count %i", ctor->getNumArgs())); } - if (auto* cast = clang::dyn_cast(expr)) { - XLS_ASSIGN_OR_RETURN(CValue arr_val, GenerateIR_Expr(cast->getBase(), loc)); + if (auto* array_subscript = + clang::dyn_cast(expr)) { + XLS_ASSIGN_OR_RETURN( + IOOpReturn ret, + InterceptIOOp(array_subscript, GetLoc(*array_subscript))); + // If this call is an IO op, then return the IO value, rather than + // generating the call. + if (!ret.generate_expr) { + return ret.value; + } + + XLS_ASSIGN_OR_RETURN(CValue arr_val, + GenerateIR_Expr(array_subscript->getBase(), loc)); + // Implicit dereference if (arr_val.type()->Is() || arr_val.type()->Is()) { XLSCC_CHECK_NE(arr_val.lvalue(), nullptr, loc); XLS_ASSIGN_OR_RETURN(arr_val, GenerateIR_Expr(arr_val.lvalue(), loc)); } - XLS_ASSIGN_OR_RETURN(CValue index_bval, - GenerateIR_Expr(cast->getIdx(), loc)); + GenerateIR_Expr(array_subscript->getIdx(), loc)); return GetArrayElement(arr_val, index_bval.rvalue(), loc); } // Access to a struct member, for example: x.foo @@ -5380,7 +5406,11 @@ absl::Status Translator::MinSizeArraySlices(CValue& true_cv, CValue& false_cv, absl::StatusOr Translator::GenerateIR_Expr(std::shared_ptr expr, const xls::SourceInfo& loc) { + XLSCC_CHECK_NE(expr, nullptr, loc); + XLSCC_CHECK(!expr->is_null(), loc); + if (!expr->is_select()) { + XLSCC_CHECK_NE(expr->leaf(), nullptr, loc); return GenerateIR_Expr(expr->leaf(), loc); } @@ -5811,34 +5841,104 @@ absl::StatusOr Translator::GenerateIR_LocalChannel( const clang::NamedDecl* namedecl, const std::shared_ptr& channel_type, const xls::SourceInfo& loc) { - if (channel_type->GetMemorySize() > 0) { - return absl::UnimplementedError( - ErrorMessage(loc, "Internal memories unsupported")); - } - - std::string ch_name = absl::StrFormat( - "__internal_%s_%s", context().sf->clang_decl->getNameAsString(), - namedecl->getNameAsString()); - XLS_ASSIGN_OR_RETURN(xls::Type * item_type_xls, - TranslateTypeToXLS(channel_type->GetItemType(), loc)); - XLS_ASSIGN_OR_RETURN(std::optional fifo_depth, - GetAnnotationWithNonNegativeIntegerParam( - *namedecl, "hls_fifo_depth", loc)); - XLS_ASSIGN_OR_RETURN( - xls::Channel * xls_channel, - package_->CreateStreamingChannel( - ch_name, xls::ChannelOps::kSendReceive, item_type_xls, - /*initial_values=*/{}, /*fifo_config=*/ - xls::FifoConfig(/*depth=*/fifo_depth.value_or(0), /*bypass=*/true, - /*register_push_outputs=*/false, - /*register_pop_outputs=*/false), - xls::FlowControl::kReadyValid)); - - unused_xls_channel_ops_.push_back({xls_channel, /*is_send=*/true}); - unused_xls_channel_ops_.push_back({xls_channel, /*is_send=*/false}); + std::string ch_name; + ChannelBundle bundle; IOChannel new_channel; new_channel.item_type = channel_type->GetItemType(); + + if (channel_type->GetMemorySize() > 0) { + ch_name = namedecl->getNameAsString(); + new_channel.memory_size = channel_type->GetMemorySize(); + + xls::ChannelStrictness strictness = + xls::ChannelStrictness::kProvenMutuallyExclusive; + XLS_ASSIGN_OR_RETURN(strictness, + GetChannelStrictness(*namedecl, channel_options_, + unused_strictness_options_)); + + const std::string& memory_name = ch_name; + XLS_ASSIGN_OR_RETURN(xls::Type * data_type, + TranslateTypeToXLS(channel_type->GetItemType(), loc)); + + XLS_ASSIGN_OR_RETURN(xls::Type * read_request_type, + channel_type->GetReadRequestType(package_, data_type)); + XLS_ASSIGN_OR_RETURN( + bundle.read_request, + package_->CreateStreamingChannel( + memory_name + "_read_request", xls::ChannelOps::kSendOnly, + read_request_type, + /*initial_values=*/{}, /*fifo_config=*/std::nullopt, + xls::FlowControl::kReadyValid, + /*strictness=*/strictness)); + unused_xls_channel_ops_.push_back({bundle.read_request, /*is_send=*/true}); + + XLS_ASSIGN_OR_RETURN( + xls::Type * read_response_type, + channel_type->GetReadResponseType(package_, data_type)); + XLS_ASSIGN_OR_RETURN( + bundle.read_response, + package_->CreateStreamingChannel( + memory_name + "_read_response", xls::ChannelOps::kReceiveOnly, + read_response_type, + /*initial_values=*/{}, /*fifo_config=*/std::nullopt, + xls::FlowControl::kReadyValid, + /*strictness=*/strictness)); + unused_xls_channel_ops_.push_back( + {bundle.read_response, /*is_send=*/false}); + + XLS_ASSIGN_OR_RETURN( + xls::Type * write_request_type, + channel_type->GetWriteRequestType(package_, data_type)); + XLS_ASSIGN_OR_RETURN( + bundle.write_request, + package_->CreateStreamingChannel( + memory_name + "_write_request", xls::ChannelOps::kSendOnly, + write_request_type, + /*initial_values=*/{}, /*fifo_config=*/std::nullopt, + xls::FlowControl::kReadyValid, + /*strictness=*/strictness)); + unused_xls_channel_ops_.push_back({bundle.write_request, /*is_send=*/true}); + + XLS_ASSIGN_OR_RETURN( + xls::Type * write_response_type, + channel_type->GetWriteResponseType(package_, data_type)); + XLS_ASSIGN_OR_RETURN( + bundle.write_response, + package_->CreateStreamingChannel( + memory_name + "_write_response", xls::ChannelOps::kReceiveOnly, + write_response_type, + /*initial_values=*/{}, /*fifo_config=*/std::nullopt, + xls::FlowControl::kReadyValid, + /*strictness=*/strictness)); + unused_xls_channel_ops_.push_back( + {bundle.write_response, /*is_send=*/false}); + } else { + ch_name = absl::StrFormat("__internal_%s_%s", + context().sf->clang_decl->getNameAsString(), + namedecl->getNameAsString()); + + XLS_ASSIGN_OR_RETURN(xls::Type * item_type_xls, + TranslateTypeToXLS(channel_type->GetItemType(), loc)); + XLS_ASSIGN_OR_RETURN(std::optional fifo_depth, + GetAnnotationWithNonNegativeIntegerParam( + *namedecl, "hls_fifo_depth", loc)); + XLS_ASSIGN_OR_RETURN( + xls::Channel * xls_channel, + package_->CreateStreamingChannel( + ch_name, xls::ChannelOps::kSendReceive, item_type_xls, + /*initial_values=*/{}, /*fifo_config=*/ + xls::FifoConfig(/*depth=*/fifo_depth.value_or(0), /*bypass=*/true, + /*register_push_outputs=*/false, + /*register_pop_outputs=*/false), + xls::FlowControl::kReadyValid)); + + unused_xls_channel_ops_.push_back({xls_channel, /*is_send=*/true}); + unused_xls_channel_ops_.push_back({xls_channel, /*is_send=*/false}); + + bundle = ChannelBundle{.regular = xls_channel}; + } + new_channel.unique_name = ch_name; new_channel.internal_to_function = true; @@ -5848,7 +5948,6 @@ absl::StatusOr Translator::GenerateIR_LocalChannel( CValue cval(/*rvalue=*/TrackedBValue(), channel_type, /*disable_type_check=*/true, lvalue); - ChannelBundle bundle = {.regular = xls_channel}; external_channels_by_internal_channel_.insert( std::make_pair(new_channel_ptr, bundle)); @@ -5866,16 +5965,19 @@ absl::Status Translator::GenerateIR_StaticDecl(const clang::VarDecl* vard, XLS_ASSIGN_OR_RETURN(std::shared_ptr obj_type, TranslateTypeFromClang(stripped.base, loc)); + std::shared_ptr channel_type; + + // Check that this is the declaration of a channel type and that it's not + // initialized if (obj_type->Is() && - clang::dyn_cast(vard->getAnyInitializer())) { - std::vector annotate_attrs = - GetClangAnnotations(*vard); + ((vard->getAnyInitializer() != nullptr && + clang::dyn_cast(vard->getAnyInitializer()) != + nullptr) || + obj_type->As()->GetMemorySize() > 0)) { + channel_type = std::dynamic_pointer_cast(obj_type); - XLS_ASSIGN_OR_RETURN( - CValue generated, - GenerateIR_LocalChannel( - namedecl, std::dynamic_pointer_cast(obj_type), - loc)); + XLS_ASSIGN_OR_RETURN(CValue generated, GenerateIR_LocalChannel( + namedecl, channel_type, loc)); return DeclareVariable(namedecl, generated, loc); } @@ -6795,16 +6897,19 @@ absl::StatusOr> Translator::TranslateTypeFromClang( XLSCC_CHECK_NE(type_attr, nullptr, loc); std::string annotation = type_attr->getAnnotation().str(); + std::shared_ptr ret; + + // NOTE: TypeIsChannel() may also need updating when changing this - if (annotation != "hls_array_as_tuple") { + if (annotation == "hls_array_as_tuple") { + XLS_ASSIGN_OR_RETURN(ret, + TranslateTypeFromClang(lval->desugar(), loc, + /*array_as_tuple=*/true)); + } else { return absl::UnimplementedError( ErrorMessage(loc, "Unsupported attribute on type: %s", annotation)); } - XLS_ASSIGN_OR_RETURN(std::shared_ptr ret, - TranslateTypeFromClang(lval->desugar(), loc, - /*array_as_tuple=*/true)); - return ret; } type->dump(); @@ -7098,6 +7203,14 @@ absl::StatusOr Translator::GenTypeConvert( XLS_ASSIGN_OR_RETURN(auto t, ResolveTypeInstance(out_type)); return GenTypeConvert(in, t, loc); } + if (out_type->Is() && in.type()->Is()) { + auto channel_type = in.type()->As(); + if (channel_type->GetMemorySize() <= 0) { + return absl::InvalidArgumentError(ErrorMessage( + loc, "Channel to pointer conversion is only suported for memories")); + } + return in.rvalue(); + } return absl::UnimplementedError( ErrorMessage(loc, "Don't know how to convert %s to type %s", in.debug_string().c_str(), std::string(*out_type))); diff --git a/xls/contrib/xlscc/translator.h b/xls/contrib/xlscc/translator.h index b1c7fde34b..193da230ee 100644 --- a/xls/contrib/xlscc/translator.h +++ b/xls/contrib/xlscc/translator.h @@ -638,6 +638,12 @@ class Translator final : public GeneratorBase, // so that IO operations can be generated without calling GenerateIR_Block() bool io_test_mode_ = false; + // These are members so that local channels can also have strictness optionss + // applied as they are created. + xlscc::ChannelOptions channel_options_; + absl::flat_hash_map + unused_strictness_options_; + const int64_t kNumSubBlockModeBits = 8; struct InstTypeHash { diff --git a/xls/contrib/xlscc/translator_types.cc b/xls/contrib/xlscc/translator_types.cc index 0340e6555e..0a00676025 100644 --- a/xls/contrib/xlscc/translator_types.cc +++ b/xls/contrib/xlscc/translator_types.cc @@ -929,7 +929,7 @@ int CChannelType::GetBitWidth() const { return item_type_->GetBitWidth(); } std::shared_ptr CChannelType::GetItemType() const { return item_type_; } CChannelType::operator std::string() const { - if (op_type_ == OpType::kRead || op_type_ == OpType::kWrite) { + if (memory_size_ > 0) { return absl::StrFormat("memory<%s,%i>", string(*item_type_), memory_size_); } return absl::StrFormat("channel<%s,%s>", string(*item_type_), diff --git a/xls/contrib/xlscc/translator_types.h b/xls/contrib/xlscc/translator_types.h index b44aa9b8ec..3d441a932c 100644 --- a/xls/contrib/xlscc/translator_types.h +++ b/xls/contrib/xlscc/translator_types.h @@ -578,6 +578,7 @@ class LValue { } bool is_select() const { return is_select_; } + bool is_channel() const { return channel_leaf_ != nullptr; } const TrackedBValue& cond() const { return cond_; } std::shared_ptr lvalue_true() const { return lvalue_true_; } std::shared_ptr lvalue_false() const { return lvalue_false_; } diff --git a/xls/contrib/xlscc/unit_tests/translator_memory_test.cc b/xls/contrib/xlscc/unit_tests/translator_memory_test.cc index 2b4429749c..2a340adf53 100644 --- a/xls/contrib/xlscc/unit_tests/translator_memory_test.cc +++ b/xls/contrib/xlscc/unit_tests/translator_memory_test.cc @@ -116,9 +116,7 @@ TEST_F(TranslatorMemoryTest, MemoryReadStructExplicitIOOp) { TEST_F(TranslatorMemoryTest, MemoryReadIOOpSubroutine) { const std::string content = R"( - #include "/xls_builtin.h" - - int ReadIt(__xls_memory& mem, int addr) { + int ReadIt(__xls_memory& mem, int addr) { return mem[addr]; } @@ -127,7 +125,7 @@ TEST_F(TranslatorMemoryTest, MemoryReadIOOpSubroutine) { __xls_memory& memory, __xls_channel& out) { const int addr = in.read(); - const int val = memory[addr]; + const int val = ReadIt(memory, addr); out.write(3*val); })"; @@ -1641,5 +1639,285 @@ TEST_F(TranslatorMemoryTest, PassChannelAliased) { IOOpTest("out", 30, true)}); } +TEST_F(TranslatorMemoryTest, MemoryLocallyDeclared) { + const std::string content = R"( + + #pragma hls_top + void foo(__xls_channel& out) { + static __xls_memory foo_store; + out.write(foo_store[13] + 3); + })"; + + HLSBlock block_spec; + { + block_spec.set_name("foo"); + + HLSChannel* ch_in = block_spec.add_channels(); + ch_in->set_name("foo_store"); + ch_in->set_type(CHANNEL_TYPE_MEMORY); + ch_in->set_depth(21); + + HLSChannel* ch_out = block_spec.add_channels(); + ch_out->set_name("out"); + ch_out->set_is_input(false); + ch_out->set_type(CHANNEL_TYPE_FIFO); + } + + absl::flat_hash_map> inputs; + inputs["foo_store_read_response"] = { + xls::Value::Tuple({xls::Value(xls::SBits(100, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(50, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(12, 16))})}; + + absl::flat_hash_map> outputs; + outputs["out"] = {xls::Value(xls::SBits(103, 32)), + xls::Value(xls::SBits(53, 32)), + xls::Value(xls::SBits(15, 32))}; + outputs["foo_store_read_request"] = { + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + })}; + + ProcTest(content, block_spec, inputs, outputs); +} + +TEST_F(TranslatorMemoryTest, MemoryLocallyDeclaredAsArrayRead) { + const std::string content = R"( + + #pragma hls_top + void foo(__xls_channel& out) { + static short foo_store[21] [[clang::annotate_type("hls_memory")]]; + out.write(foo_store[13] + 3); + })"; + + HLSBlock block_spec; + { + block_spec.set_name("foo"); + + HLSChannel* ch_in = block_spec.add_channels(); + ch_in->set_name("foo_store"); + ch_in->set_type(CHANNEL_TYPE_MEMORY); + ch_in->set_depth(21); + + HLSChannel* ch_out = block_spec.add_channels(); + ch_out->set_name("out"); + ch_out->set_is_input(false); + ch_out->set_type(CHANNEL_TYPE_FIFO); + } + + absl::flat_hash_map> inputs; + inputs["foo_store_read_response"] = { + xls::Value::Tuple({xls::Value(xls::SBits(100, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(50, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(12, 16))})}; + + absl::flat_hash_map> outputs; + outputs["out"] = {xls::Value(xls::SBits(103, 32)), + xls::Value(xls::SBits(53, 32)), + xls::Value(xls::SBits(15, 32))}; + outputs["foo_store_read_request"] = { + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + })}; + + ProcTest(content, block_spec, inputs, outputs); +} + +TEST_F(TranslatorMemoryTest, MemoryLocallyDeclaredAsArrayWrite) { + const std::string content = R"( + + #pragma hls_top + void foo(__xls_channel& in) { + static short foo_store[21] [[clang::annotate_type("hls_memory")]]; + foo_store[13] = in.read() + 3; + })"; + + HLSBlock block_spec; + { + block_spec.set_name("foo"); + + HLSChannel* ch_in = block_spec.add_channels(); + ch_in->set_name("foo_store"); + ch_in->set_type(CHANNEL_TYPE_MEMORY); + ch_in->set_depth(21); + + HLSChannel* ch_out = block_spec.add_channels(); + ch_out->set_name("in"); + ch_out->set_is_input(true); + ch_out->set_type(CHANNEL_TYPE_FIFO); + } + + absl::flat_hash_map> inputs; + inputs["foo_store_write_response"] = { + xls::Value::Tuple({}), xls::Value::Tuple({}), xls::Value::Tuple({})}; + inputs["in"] = {xls::Value(xls::SBits(100, 32)), + xls::Value(xls::SBits(50, 32)), + xls::Value(xls::SBits(12, 32))}; + + absl::flat_hash_map> outputs; + outputs["foo_store_write_request"] = { + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value(xls::SBits(103, 16)), // value + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value(xls::SBits(53, 16)), // value + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value(xls::SBits(15, 16)), // value + xls::Value::Tuple({}) // mask + })}; + + ProcTest(content, block_spec, inputs, outputs); +} + +TEST_F(TranslatorMemoryTest, MemoryLocallyDeclaredAsArraySubroutine) { + const std::string content = R"( + short sub( + short (&foo_store)[21] [[clang::annotate_type("hls_memory")]]) { + return foo_store[13] + 3; + } + + #pragma hls_top + void foo(__xls_channel& out) { + static short foo_store[21] [[clang::annotate_type("hls_memory")]]; + const short x = sub(foo_store); + out.write(x); + })"; + + HLSBlock block_spec; + { + block_spec.set_name("foo"); + + HLSChannel* ch_in = block_spec.add_channels(); + ch_in->set_name("foo_store"); + ch_in->set_type(CHANNEL_TYPE_MEMORY); + ch_in->set_depth(21); + + HLSChannel* ch_out = block_spec.add_channels(); + ch_out->set_name("out"); + ch_out->set_is_input(false); + ch_out->set_type(CHANNEL_TYPE_FIFO); + } + + absl::flat_hash_map> inputs; + inputs["foo_store_read_response"] = { + xls::Value::Tuple({xls::Value(xls::SBits(100, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(50, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(12, 16))})}; + + absl::flat_hash_map> outputs; + outputs["out"] = {xls::Value(xls::SBits(103, 32)), + xls::Value(xls::SBits(53, 32)), + xls::Value(xls::SBits(15, 32))}; + outputs["foo_store_read_request"] = { + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + })}; + + ProcTest(content, block_spec, inputs, outputs); +} + +TEST_F(TranslatorMemoryTest, MemoryLocallyDeclaredAsArraySubroutine2) { + const std::string content = R"( + short sub( + short foo_store[21] [[clang::annotate_type("hls_memory")]]) { + return foo_store[13] + 3; + } + + #pragma hls_top + void foo(__xls_channel& out) { + static short foo_store[21] [[clang::annotate_type("hls_memory")]]; + const short x = sub(foo_store); + out.write(x); + })"; + + HLSBlock block_spec; + { + block_spec.set_name("foo"); + + HLSChannel* ch_in = block_spec.add_channels(); + ch_in->set_name("foo_store"); + ch_in->set_type(CHANNEL_TYPE_MEMORY); + ch_in->set_depth(21); + + HLSChannel* ch_out = block_spec.add_channels(); + ch_out->set_name("out"); + ch_out->set_is_input(false); + ch_out->set_type(CHANNEL_TYPE_FIFO); + } + + absl::flat_hash_map> inputs; + inputs["foo_store_read_response"] = { + xls::Value::Tuple({xls::Value(xls::SBits(100, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(50, 16))}), + xls::Value::Tuple({xls::Value(xls::SBits(12, 16))})}; + + absl::flat_hash_map> outputs; + outputs["out"] = {xls::Value(xls::SBits(103, 32)), + xls::Value(xls::SBits(53, 32)), + xls::Value(xls::SBits(15, 32))}; + outputs["foo_store_read_request"] = { + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + }), + xls::Value::Tuple({ + xls::Value(xls::UBits(13, 5)), // addr + xls::Value::Tuple({}) // mask + })}; + + ProcTest(content, block_spec, inputs, outputs); +} + +TEST_F(TranslatorMemoryTest, AddressOfMemoryAsArray) { + const std::string content = R"( + #pragma hls_top + void foo(__xls_channel& out) { + static short foo_store[21] [[clang::annotate_type("hls_memory")]]; + short* x = &foo_store[10]; + out.write(x[1]); + })"; + + ASSERT_THAT(SourceToIr(content).status(), + absl_testing::StatusIs(absl::StatusCode::kInvalidArgument, + testing::HasSubstr("only supported"))); +} + } // namespace } // namespace xlscc