15 #include "fletchgen/array.h"
17 #include <cerata/api.h>
18 #include <cerata/vhdl/vhdl.h>
19 #include <fletcher/common.h>
26 #include "fletchgen/bus.h"
27 #include "fletchgen/basic_types.h"
35 using cerata::boolean;
39 using cerata::integer;
40 using cerata::parameter;
44 PARAM_FACTORY(index_width)
45 PARAM_FACTORY(tag_width)
49 static const int ARROW_OFFSET_WIDTH = 32;
52 fletcher::FieldMetadata field_meta;
53 fletcher::FieldAnalyzer fa(&field_meta);
55 return field_meta.buffers.size();
59 return fletcher::GetUIntMeta(field, fletcher::meta::TAG_WIDTH, 1);
62 std::shared_ptr<Type>
cmd_type(
const std::shared_ptr<Node> &index_width,
63 const std::shared_ptr<Node> &tag_width,
64 const std::optional<std::shared_ptr<Node>> &ctrl_width) {
66 auto data = record({field(
"firstIdx", vector(index_width)),
67 field(
"lastIdx", vector(index_width)),
68 field(
"tag", vector(tag_width))});
72 auto ctrl = field(vector(
"ctrl", *ctrl_width));
73 data->AddField(ctrl, 2);
76 auto result = stream(
data);
80 std::shared_ptr<Type>
unlock_type(
const std::shared_ptr<Node> &tag_width) {
81 std::shared_ptr<Type> unlock_stream = stream(
"tag", vector(tag_width));
86 auto data_stream = stream(
"ar_out",
"", record({field(
data(full_width)),
87 field(
dvalid(num_streams,
true)),
88 field(
last(num_streams,
true))}),
89 {field(
"valid", vector(num_streams)),
90 field(
"ready", vector(num_streams))->Reverse()});
94 std::shared_ptr<Type>
array_writer_in(uint32_t num_streams, uint32_t full_width) {
95 auto data_stream = stream(
"aw_in",
"", record({field(
data(full_width)),
96 field(
dvalid(num_streams,
true)),
97 field(
last(num_streams,
true))}),
98 {field(
"valid", vector(num_streams)),
99 field(
"ready", vector(num_streams))->Reverse()});
113 static std::string ArrayName(fletcher::Mode mode) {
114 return mode == Mode::READ ?
"ArrayReader" :
"ArrayWriter";
117 static std::string DataName(fletcher::Mode mode) {
118 return mode == Mode::READ ?
"out" :
"in";
141 auto optional_existing = cerata::default_component_pool()->Get(ArrayName(mode));
142 if (optional_existing) {
143 return *optional_existing;
146 auto result = cerata::component(ArrayName(mode));
152 auto iw = index_width();
153 auto tw = tag_width();
154 tw->SetName(
"CMD_TAG_WIDTH");
157 parameter(
"CFG", std::string(
"")),
158 parameter(
"CMD_TAG_ENABLE",
true),
162 auto bcd = port(
"bcd",
cr(), Port::Dir::IN,
bus_cd());
163 auto kcd = port(
"kcd",
cr(), Port::Dir::IN,
kernel_cd());
166 auto cmd = port(
"cmd",
cmd_type(iw, tw, strl(
"arcfg_ctrlWidth(CFG, BUS_ADDR_WIDTH)")), Port::Dir::IN,
kernel_cd());
176 auto dir = mode == Mode::READ ? Port::Dir::OUT : Port::Dir::IN;
180 result->Add({bcd, kcd, cmd, unlock,
bus,
data});
182 result->SetMeta(cerata::vhdl::meta::PRIMITIVE,
"true");
183 result->SetMeta(cerata::vhdl::meta::LIBRARY,
"work");
184 result->SetMeta(cerata::vhdl::meta::PACKAGE,
"Array_pkg");
189 if (type.id() == arrow::Type::LIST) {
212 case arrow::Type::BOOL:
return intl(1);
213 case arrow::Type::DATE32:
return intl(32);
214 case arrow::Type::DATE64:
return intl(64);
215 case arrow::Type::DOUBLE:
return intl(64);
216 case arrow::Type::FLOAT:
return intl(32);
217 case arrow::Type::HALF_FLOAT:
return intl(16);
218 case arrow::Type::INT8:
return intl(8);
219 case arrow::Type::INT16:
return intl(16);
220 case arrow::Type::INT32:
return intl(32);
221 case arrow::Type::INT64:
return intl(64);
222 case arrow::Type::TIME32:
return intl(32);
223 case arrow::Type::TIME64:
return intl(64);
224 case arrow::Type::TIMESTAMP:
return intl(64);
225 case arrow::Type::UINT8:
return intl(8);
226 case arrow::Type::UINT16:
return intl(16);
227 case arrow::Type::UINT32:
return intl(32);
228 case arrow::Type::UINT64:
return intl(64);
229 case arrow::Type::DECIMAL:
return intl(128);
232 case arrow::Type::LIST:
return strl(
"OFFSET_WIDTH");
233 case arrow::Type::BINARY:
return strl(
"OFFSET_WIDTH");
234 case arrow::Type::STRING:
return strl(
"OFFSET_WIDTH");
243 throw std::domain_error(
"Arrow type " + type.ToString() +
" not supported.");
246 case arrow::Type::STRUCT:
return intl(0);
249 case arrow::Type::FIXED_SIZE_BINARY: {
250 const auto *t =
dynamic_cast<const arrow::FixedSizeBinaryType *
>(&type);
251 return intl(t->bit_width());
260 if (field.nullable()) {
265 int epc = fletcher::GetUIntMeta(field, fletcher::meta::VALUE_EPC, 1);
266 int lepc = fletcher::GetUIntMeta(field, fletcher::meta::LIST_EPC, 1);
268 bool has_children =
false;
272 ret +=
"prim(" + w->ToString();
278 if ((field.type()->id() == arrow::Type::BINARY) || (field.type()->id() == arrow::Type::STRING)) {
294 if (epc > 1 || lepc > 1) {
298 ret +=
"epc=" + std::to_string(epc);
304 ret +=
"lepc=" + std::to_string(epc);
309 for (
int c = 0; c < field.type()->num_fields(); c++) {
310 auto child = field.type()->field(c);
312 if (c != field.type()->num_fields() - 1)
317 for (; level > 0; level--)
324 auto result = TypeMapper::Make(stream_type, other);
326 constexpr
size_t idx_valid = 1;
327 constexpr
size_t idx_ready = 2;
328 constexpr
size_t idx_data = 4;
329 constexpr
size_t idx_dvalid = 5;
330 constexpr
size_t idx_last = 6;
332 auto flat_stream = result->flat_a();
333 for (
size_t i = 0; i < flat_stream.size(); i++) {
334 auto t = flat_stream[i].type_;
335 if (t->Is(Type::RECORD)) {
336 }
else if (t == cerata::Stream::valid().get()) {
337 result->Add(i, idx_valid);
338 }
else if (t == cerata::Stream::ready().get()) {
339 result->Add(i, idx_ready);
340 }
else if (t->name() ==
dvalid()->name()) {
341 result->Add(i, idx_dvalid);
342 }
else if (t->name() ==
last()->name()) {
343 result->Add(i, idx_last);
346 result->Add(i, idx_data);
352 std::shared_ptr<Type> ListPrimType(
int val_epc,
int list_epc,
int val_width,
int idx_width,
const std::string &name) {
353 auto data_width = val_epc * val_width;
354 auto lengths_width = list_epc * idx_width;
356 auto e_count_width =
static_cast<int>(ceil(log2(val_epc + 1)));
357 auto l_count_width =
static_cast<int>(ceil(log2(list_epc + 1)));
359 return record({field(
"", stream(record({field(
"dvalid",
dvalid()),
360 field(
"last",
last()),
361 field(
"length",
length(lengths_width)),
362 field(
"count",
count(l_count_width))}))),
363 field(name, stream(record({field(
"dvalid",
dvalid()),
364 field(
"last",
last()),
365 field(
"",
data(data_width)),
366 field(
"count",
count(e_count_width))})))});
369 std::shared_ptr<Type>
GetStreamType(
const arrow::Field &arrow_field, fletcher::Mode mode,
int level) {
379 int epc = fletcher::GetUIntMeta(arrow_field, fletcher::meta::VALUE_EPC, 1);
380 int lepc = fletcher::GetUIntMeta(arrow_field, fletcher::meta::LIST_EPC, 1);
383 auto e_count_width =
static_cast<int>(ceil(log2(epc + 1)));
384 auto l_count_width =
static_cast<int>(ceil(log2(lepc + 1)));
387 std::shared_ptr<Type> type;
390 switch (arrow_field.type()->id()) {
394 case arrow::Type::BINARY:
return ListPrimType(epc, lepc, 8, ARROW_OFFSET_WIDTH,
"bytes");
399 case arrow::Type::STRING:
return ListPrimType(epc, lepc, 8, ARROW_OFFSET_WIDTH,
"chars");
404 case arrow::Type::LIST: {
406 if (arrow_field.type()->num_fields() != 1) {
407 FLETCHER_LOG(FATAL,
"Encountered Arrow list type with other than 1 child.");
410 auto child_field = arrow_field.type()->field(0);
414 FLETCHER_LOG(DEBUG,
"Using \"listprim\" configuration for list of non-nullable primitives of width " << w);
416 return ListPrimType(epc, lepc, w, ARROW_OFFSET_WIDTH, child_field->name());
420 if ((epc > 1) || (lepc > 1)) {
421 FLETCHER_LOG(FATAL,
"(Length)-elements-per-cycle > 1 on non-primitive list is not supported.");
423 auto values_type =
GetStreamType(*child_field, mode, level + 1);
424 auto child = stream(record({field(
"dvalid",
dvalid()),
425 field(
"last",
last()),
426 field(
"data", values_type),
427 field(
"count",
count(e_count_width))}));
428 type = record({field(
"length",
length(ARROW_OFFSET_WIDTH)),
429 field(child_field->name(), child)});
430 e_count_width = l_count_width;
436 case arrow::Type::STRUCT: {
437 if (arrow_field.type()->num_fields() < 1) {
438 FLETCHER_LOG(FATAL,
"Encountered Arrow struct type without any children.");
440 std::vector<std::shared_ptr<cerata::Field>> children;
441 for (
const auto &f : arrow_field.type()->fields()) {
443 children.push_back(field(f->name(), child_type));
445 type = record(arrow_field.name() +
"_rec", children);
459 auto rec = record({field(
"dvalid",
dvalid()),
460 field(
"last",
last())});
461 if (arrow_field.nullable()) {
462 rec->AddField(field(
"validity", validity()));
465 rec->AddField(field(
"", type));
468 rec->AddField(field(
"count",
count(e_count_width)));
480 uint32_t epc = fletcher::GetUIntMeta(arrow_field, fletcher::meta::VALUE_EPC, 1);
481 uint32_t lepc = fletcher::GetUIntMeta(arrow_field, fletcher::meta::LIST_EPC, 1);
483 auto e_count_width =
static_cast<int>(ceil(log2(epc + 1)));
484 auto l_count_width =
static_cast<int>(ceil(log2(lepc + 1)));
486 uint32_t validity_bit = arrow_field.nullable() ? 1 : 0;
488 switch (arrow_field.type()->id()) {
489 case arrow::Type::BINARY: {
490 auto data_width = epc * 8;
491 auto length_width = lepc * 32;
492 return {2, e_count_width + l_count_width + data_width + length_width + validity_bit};
495 case arrow::Type::STRING: {
496 auto data_width = epc * 8;
497 auto length_width = lepc * 32;
498 return {2, e_count_width + l_count_width + data_width + length_width + validity_bit};
502 case arrow::Type::LIST: {
503 auto child_field = arrow_field.type()->field(0);
506 return {2, e_count_width + l_count_width + data_width * epc + ARROW_OFFSET_WIDTH * lepc + validity_bit};
508 auto arrow_child = arrow_field.type()->field(0);
511 return {elem_spec.first + 1, elem_spec.second + ARROW_OFFSET_WIDTH + validity_bit};
516 case arrow::Type::STRUCT: {
518 FLETCHER_LOG(ERROR,
"Multi-elements-per-cycle at struct-level is unsupported."
519 "Try to set EPC > 1 at struct field level.");
522 FLETCHER_LOG(ERROR,
"Struct delivers no length stream.");
524 if (arrow_field.type()->num_fields() < 1) {
525 FLETCHER_LOG(ERROR,
"Encountered Arrow struct type without any children.");
527 auto spec = std::pair<int, int>{0, 0};
528 for (
const auto &f : arrow_field.type()->fields()) {
530 spec.first += child_spec.first;
531 spec.second += child_spec.second;
538 auto fwt = std::dynamic_pointer_cast<arrow::FixedWidthType>(arrow_field.type());
539 if (fwt ==
nullptr) {
540 FLETCHER_LOG(ERROR,
"Unsupported Arrow type: " + arrow_field.type()->ToString());
542 return {1, (epc > 1 ? e_count_width : 0) + epc * (fwt->bit_width() + validity_bit)};
Contains all classes and functions related to Fletchgen.
std::shared_ptr< Type > cmd_type(const std::shared_ptr< Node > &index_width, const std::shared_ptr< Node > &tag_width, const std::optional< std::shared_ptr< Node >> &ctrl_width)
Return a Fletcher command stream type.
std::shared_ptr< ClockDomain > kernel_cd()
Fletcher accelerator clock domain.
std::shared_ptr< TypeMapper > GetStreamTypeMapper(Type *stream_type, Type *other)
Get a type mapper for an Arrow::Field-based stream to an ArrayReader/Writer stream.
std::string GenerateConfigString(const arrow::Field &field, int level)
Return the configuration string for a ArrayReader/Writer.
std::shared_ptr< Node > GetWidthNode(const arrow::DataType &type)
Return a node representing the width of a (flat) Arrow DataType.
std::pair< uint32_t, uint32_t > GetArrayDataSpec(const arrow::Field &arrow_field)
Get the ArrayR/W number of streams and data width from an Arrow Field.
std::shared_ptr< Type > cr()
Fletcher clock/reset;.
std::shared_ptr< Type > data(int width)
Fletcher data.
std::shared_ptr< Type > unlock_type(const std::shared_ptr< Node > &tag_width)
Fletcher unlock stream.
std::shared_ptr< Type > bus(const BusSpecParams &spec)
Fletcher bus type with access mode conveyed through spec of params.
std::shared_ptr< Type > ConvertFixedWidthType(const std::shared_ptr< arrow::DataType > &arrow_type, int epc)
Convert a fixed-width arrow::DataType to a fixed-width Fletcher Type.
std::shared_ptr< Type > dvalid(int width, bool on_primitive)
Fletcher dvalid.
std::shared_ptr< Type > GetStreamType(const arrow::Field &arrow_field, fletcher::Mode mode, int level)
Convert an Arrow::Field into a stream type.
std::shared_ptr< Type > length(int width)
Fletcher length.
std::shared_ptr< Type > count(int width)
Fletcher count.
uint32_t GetTagWidth(const arrow::Field &field)
Return the tag width of this field as a literal node. Settable through Arrow metadata....
ConfigType
Types for ArrayReader/Writer configuration string.
@ LIST
Variable length fields.
@ PRIM
Primitive (fixed-width) fields.
@ LIST_PRIM
List of primitives. Can have EPC > 1.
@ STRUCT
Structs, composed of multiple fields.
std::shared_ptr< Type > array_writer_in(uint32_t num_streams, uint32_t full_width)
Fletcher write data.
size_t GetCtrlBufferCount(const arrow::Field &field)
Return the number of buffers for the control field.
Component * array(Mode mode)
Return a Cerata component model of an Array(Reader/Writer).
int GetFixedWidthTypeBitWidth(const arrow::DataType &arrow_type)
Returns the bit-width of a fixed-width Arrow type. Throws if it's not a fixed-width type.
std::shared_ptr< ClockDomain > bus_cd()
Fletcher bus clock domain.
std::shared_ptr< Type > last(int width, bool on_primitive)
Fletcher last.
std::shared_ptr< BusPort > bus_port(const std::string &name, Port::Dir dir, const BusSpecParams ¶ms)
Make a new port and return a shared pointer to it.
@ READ
Interface reads from memory.
@ WRITE
Interface writes to memory.
ConfigType GetConfigType(const arrow::DataType &type)
Return the configuration string type version of an arrow::DataType.
std::shared_ptr< Type > array_reader_out(uint32_t num_streams, uint32_t full_width)
Fletcher read data.
Holds bus parameters based on bus dimensions, that has actual nodes representing the dimensions.
Holds bus parameters and function based on bus dimensions, that has actual nodes representing the dim...