esiaccel 0.1.5.dev205__cp39-cp39-win_amd64.whl → 0.1.5.dev450__cp39-cp39-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. esiaccel/CosimBackend.dll +0 -0
  2. esiaccel/CosimBackend.lib +0 -0
  3. esiaccel/ESICppRuntime.dll +0 -0
  4. esiaccel/ESICppRuntime.lib +0 -0
  5. esiaccel/EsiCosimDpiServer.dll +0 -0
  6. esiaccel/EsiCosimDpiServer.lib +0 -0
  7. esiaccel/MtiPli.dll +0 -0
  8. esiaccel/MtiPli.lib +0 -0
  9. esiaccel/__init__.py +12 -3
  10. esiaccel/abseil_dll.dll +0 -0
  11. esiaccel/accelerator.py +37 -5
  12. esiaccel/cares.dll +0 -0
  13. esiaccel/codegen.py +3 -3
  14. esiaccel/cosim/Cosim_Endpoint.sv +0 -18
  15. esiaccel/cosim/driver.cpp +6 -6
  16. esiaccel/cosim/driver.sv +14 -0
  17. esiaccel/cosim/questa.py +67 -4
  18. esiaccel/cosim/simulator.py +185 -38
  19. esiaccel/cosim/verilator.py +36 -5
  20. esiaccel/esiCppAccel.cp39-win_amd64.pyd +0 -0
  21. esiaccel/esiquery.exe +0 -0
  22. esiaccel/include/esi/Accelerator.h +1 -14
  23. esiaccel/include/esi/CLI.h +5 -5
  24. esiaccel/include/esi/Common.h +11 -1
  25. esiaccel/include/esi/Context.h +17 -9
  26. esiaccel/include/esi/Manifest.h +0 -2
  27. esiaccel/include/esi/Services.h +34 -10
  28. esiaccel/include/esi/Types.h +53 -31
  29. esiaccel/include/esi/Values.h +313 -0
  30. esiaccel/libcrypto-3-x64.dll +0 -0
  31. esiaccel/libprotobuf.dll +0 -0
  32. esiaccel/libssl-3-x64.dll +0 -0
  33. esiaccel/re2.dll +0 -0
  34. esiaccel/types.py +3 -3
  35. esiaccel/zlib1.dll +0 -0
  36. {esiaccel-0.1.5.dev205.dist-info → esiaccel-0.1.5.dev450.dist-info}/METADATA +1 -1
  37. esiaccel-0.1.5.dev450.dist-info/RECORD +54 -0
  38. esiaccel-0.1.5.dev205.dist-info/RECORD +0 -46
  39. {esiaccel-0.1.5.dev205.dist-info → esiaccel-0.1.5.dev450.dist-info}/WHEEL +0 -0
  40. {esiaccel-0.1.5.dev205.dist-info → esiaccel-0.1.5.dev450.dist-info}/entry_points.txt +0 -0
  41. {esiaccel-0.1.5.dev205.dist-info → esiaccel-0.1.5.dev450.dist-info}/licenses/LICENSE +0 -0
  42. {esiaccel-0.1.5.dev205.dist-info → esiaccel-0.1.5.dev450.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@
4
4
 
5
5
  import os
6
6
  from pathlib import Path
7
- from typing import List
7
+ from typing import List, Optional, Callable, Dict
8
8
 
9
9
  from .simulator import CosimCollateralDir, Simulator, SourceFiles
10
10
 
@@ -14,9 +14,29 @@ class Verilator(Simulator):
14
14
 
15
15
  DefaultDriver = CosimCollateralDir / "driver.cpp"
16
16
 
17
- def __init__(self, sources: SourceFiles, run_dir: Path, debug: bool):
18
- super().__init__(sources, run_dir, debug)
19
-
17
+ def __init__(
18
+ self,
19
+ sources: SourceFiles,
20
+ run_dir: Path,
21
+ debug: bool,
22
+ run_stdout_callback: Optional[Callable[[str], None]] = None,
23
+ run_stderr_callback: Optional[Callable[[str], None]] = None,
24
+ compile_stdout_callback: Optional[Callable[[str], None]] = None,
25
+ compile_stderr_callback: Optional[Callable[[str], None]] = None,
26
+ make_default_logs: bool = True,
27
+ macro_definitions: Optional[Dict[str, str]] = None,
28
+ ):
29
+ super().__init__(
30
+ sources=sources,
31
+ run_dir=run_dir,
32
+ debug=debug,
33
+ run_stdout_callback=run_stdout_callback,
34
+ run_stderr_callback=run_stderr_callback,
35
+ compile_stdout_callback=compile_stdout_callback,
36
+ compile_stderr_callback=compile_stderr_callback,
37
+ make_default_logs=make_default_logs,
38
+ macro_definitions=macro_definitions,
39
+ )
20
40
  self.verilator = "verilator"
21
41
  if "VERILATOR_PATH" in os.environ:
22
42
  self.verilator = os.environ["VERILATOR_PATH"]
@@ -25,6 +45,15 @@ class Verilator(Simulator):
25
45
  cmd: List[str] = [
26
46
  self.verilator,
27
47
  "--cc",
48
+ ]
49
+
50
+ if self.macro_definitions:
51
+ cmd += [
52
+ f"+define+{k}={v}" if v is not None else f"+define+{k}"
53
+ for k, v in self.macro_definitions.items()
54
+ ]
55
+
56
+ cmd += [
28
57
  "--top-module",
29
58
  self.sources.top,
30
59
  "-DSIMULATION",
@@ -36,6 +65,7 @@ class Verilator(Simulator):
36
65
  "-j",
37
66
  "0",
38
67
  "--output-split",
68
+ "--autoflush",
39
69
  "--assert",
40
70
  str(Verilator.DefaultDriver),
41
71
  ]
@@ -44,7 +74,8 @@ class Verilator(Simulator):
44
74
  ]
45
75
  if self.debug:
46
76
  cmd += [
47
- "--trace", "--trace-params", "--trace-structs", "--trace-underscore"
77
+ "--trace-fst", "--trace-params", "--trace-structs",
78
+ "--trace-underscore"
48
79
  ]
49
80
  cflags.append("-DTRACE")
50
81
  if len(cflags) > 0:
Binary file
esiaccel/esiquery.exe CHANGED
Binary file
@@ -86,13 +86,6 @@ public:
86
86
  /// Disconnect from the accelerator cleanly.
87
87
  virtual void disconnect();
88
88
 
89
- // While building the design, keep around a std::map of active services
90
- // indexed by the service name. When a new service is encountered during
91
- // descent, add it to the table (perhaps overwriting one). Modifications to
92
- // the table only apply to the current branch, so copy this and update it at
93
- // each level of the tree.
94
- using ServiceTable = std::map<std::string, services::Service *>;
95
-
96
89
  /// Return a pointer to the accelerator 'service' thread (or threads). If the
97
90
  /// thread(s) are not running, they will be started when this method is
98
91
  /// called. `std::thread` is used. If users don't want the runtime to spin up
@@ -165,7 +158,7 @@ private:
165
158
 
166
159
  /// Cache services via a unique_ptr so they get free'd automatically when
167
160
  /// Accelerator objects get deconstructed.
168
- using ServiceCacheKey = std::tuple<const std::type_info *, AppIDPath>;
161
+ using ServiceCacheKey = std::tuple<std::string, AppIDPath>;
169
162
  std::map<ServiceCacheKey, std::unique_ptr<Service>> serviceCache;
170
163
 
171
164
  std::unique_ptr<AcceleratorServiceThread> serviceThread;
@@ -176,12 +169,6 @@ private:
176
169
 
177
170
  namespace registry {
178
171
 
179
- // Connect to an ESI accelerator given a backend name and connection specifier.
180
- // Alternatively, instantiate the backend directly (if you're using C++).
181
- std::unique_ptr<AcceleratorConnection> connect(Context &ctxt,
182
- const std::string &backend,
183
- const std::string &connection);
184
-
185
172
  namespace internal {
186
173
 
187
174
  /// Backends can register themselves to be connected via a connection string.
@@ -51,19 +51,19 @@ public:
51
51
  ctxt = Context::withLogger<ConsoleLogger>(Logger::Level::Debug);
52
52
  else if (verbose)
53
53
  ctxt = Context::withLogger<ConsoleLogger>(Logger::Level::Info);
54
+ else
55
+ ctxt = Context::withLogger<ConsoleLogger>(Logger::Level::Warning);
54
56
  return 0;
55
57
  }
56
58
 
57
59
  /// Connect to the accelerator using the specified backend and connection.
58
- std::unique_ptr<AcceleratorConnection> connect() {
59
- return ctxt.connect(backend, connStr);
60
- }
60
+ AcceleratorConnection *connect() { return ctxt->connect(backend, connStr); }
61
61
 
62
62
  /// Get the context.
63
- Context &getContext() { return ctxt; }
63
+ Context &getContext() { return *ctxt; }
64
64
 
65
65
  protected:
66
- Context ctxt;
66
+ std::unique_ptr<Context> ctxt;
67
67
 
68
68
  std::string backend;
69
69
  std::string connStr;
@@ -43,6 +43,13 @@ struct AppID {
43
43
  return name == other.name && idx == other.idx;
44
44
  }
45
45
  bool operator!=(const AppID &other) const { return !(*this == other); }
46
+ friend std::ostream &operator<<(std::ostream &os, const AppID &id);
47
+
48
+ std::string toString() const {
49
+ if (idx.has_value())
50
+ return name + "[" + std::to_string(idx.value()) + "]";
51
+ return name;
52
+ }
46
53
  };
47
54
  bool operator<(const AppID &a, const AppID &b);
48
55
 
@@ -53,6 +60,7 @@ public:
53
60
  AppIDPath operator+(const AppIDPath &b) const;
54
61
  AppIDPath parent() const;
55
62
  std::string toStr() const;
63
+ friend std::ostream &operator<<(std::ostream &os, const AppIDPath &path);
56
64
  };
57
65
  bool operator<(const AppIDPath &a, const AppIDPath &b);
58
66
 
@@ -106,6 +114,8 @@ class MessageData {
106
114
  public:
107
115
  /// Adopts the data vector buffer.
108
116
  MessageData() = default;
117
+ MessageData(std::span<const uint8_t> data)
118
+ : data(data.data(), data.data() + data.size()) {}
109
119
  MessageData(std::vector<uint8_t> &data) : data(std::move(data)) {}
110
120
  MessageData(std::vector<uint8_t> &&data) : data(std::move(data)) {}
111
121
  MessageData(const uint8_t *data, size_t size) : data(data, data + size) {}
@@ -126,6 +136,7 @@ public:
126
136
 
127
137
  /// Get the size of the data in bytes.
128
138
  size_t getSize() const { return data.size(); }
139
+ size_t size() const { return getSize(); }
129
140
 
130
141
  /// Returns true if this message contains no data.
131
142
  bool empty() const { return data.empty(); }
@@ -158,7 +169,6 @@ private:
158
169
  } // namespace esi
159
170
 
160
171
  std::ostream &operator<<(std::ostream &, const esi::ModuleInfo &);
161
- std::ostream &operator<<(std::ostream &, const esi::AppID &);
162
172
 
163
173
  //===----------------------------------------------------------------------===//
164
174
  // Functions which should be in the standard library.
@@ -20,23 +20,30 @@
20
20
  #include "esi/Types.h"
21
21
 
22
22
  #include <exception>
23
+ #include <map>
23
24
  #include <memory>
24
- #include <optional>
25
+ #include <vector>
25
26
 
26
27
  namespace esi {
27
28
  class AcceleratorConnection;
28
29
 
29
30
  /// AcceleratorConnections, Accelerators, and Manifests must all share a
30
- /// context. It owns all the types, uniquifying them.
31
+ /// context. It owns all the types, uniquifying them. It also owns the
32
+ /// connections (which own the Accelerators). When it is destroyed, all
33
+ /// connections are disconnected and the objects are destroyed.
31
34
  class Context {
32
35
  public:
33
- Context() : logger(std::make_unique<ConsoleLogger>(Logger::Level::Warning)) {}
34
- Context(std::unique_ptr<Logger> logger) : logger(std::move(logger)) {}
36
+ Context();
37
+ Context(std::unique_ptr<Logger> logger);
38
+ ~Context();
39
+
40
+ /// Disconnect from all accelerators associated with this context.
41
+ void disconnectAll();
35
42
 
36
43
  /// Create a context with a specific logger type.
37
44
  template <typename T, typename... Args>
38
- static Context withLogger(Args &&...args) {
39
- return Context(std::make_unique<T>(args...));
45
+ static std::unique_ptr<Context> withLogger(Args &&...args) {
46
+ return std::make_unique<Context>(std::make_unique<T>(args...));
40
47
  }
41
48
 
42
49
  /// Resolve a type id to the type.
@@ -49,9 +56,9 @@ public:
49
56
  /// Register a type with the context. Takes ownership of the pointer type.
50
57
  void registerType(Type *type);
51
58
 
52
- /// Connect to an accelerator backend.
53
- std::unique_ptr<AcceleratorConnection> connect(std::string backend,
54
- std::string connection);
59
+ /// Connect to an accelerator backend. Retains ownership internally and
60
+ /// returns a non-owning pointer.
61
+ AcceleratorConnection *connect(std::string backend, std::string connection);
55
62
 
56
63
  /// Register a logger with the accelerator. Assumes ownership of the logger.
57
64
  void setLogger(std::unique_ptr<Logger> logger) {
@@ -63,6 +70,7 @@ public:
63
70
 
64
71
  private:
65
72
  std::unique_ptr<Logger> logger;
73
+ std::vector<std::unique_ptr<AcceleratorConnection>> connections;
66
74
 
67
75
  private:
68
76
  using TypeCache = std::map<Type::ID, std::unique_ptr<Type>>;
@@ -65,8 +65,6 @@ private:
65
65
 
66
66
  } // namespace esi
67
67
 
68
- std::ostream &operator<<(std::ostream &os, const esi::AppID &id);
69
- std::ostream &operator<<(std::ostream &, const esi::AppIDPath &);
70
68
  std::ostream &operator<<(std::ostream &, const esi::ModuleInfo &);
71
69
 
72
70
  #endif // ESI_MANIFEST_H
@@ -25,10 +25,21 @@
25
25
  #include "esi/Ports.h"
26
26
 
27
27
  #include <cstdint>
28
+ #include <list>
28
29
 
29
30
  namespace esi {
30
31
  class AcceleratorConnection;
31
32
  class Engine;
33
+ namespace services {
34
+ class Service;
35
+ }
36
+
37
+ // While building the design, keep around a std::map of active services indexed
38
+ // by the service name. When a new service is encountered during descent, add it
39
+ // to the table (perhaps overwriting one). Modifications to the table only apply
40
+ // to the current branch, so copy this and update it at each level of the tree.
41
+ using ServiceTable = std::map<std::string, services::Service *>;
42
+
32
43
  namespace services {
33
44
 
34
45
  /// Add a custom interface to a service client at a particular point in the
@@ -365,18 +376,23 @@ public:
365
376
  virtual std::string getServiceSymbol() const override;
366
377
  virtual BundlePort *getPort(AppIDPath id,
367
378
  const BundleType *type) const override;
379
+ virtual Service *getChildService(Service::Type service, AppIDPath id = {},
380
+ std::string implName = {},
381
+ ServiceImplDetails details = {},
382
+ HWClientDetails clients = {}) override;
383
+ MMIO::MMIORegion *getMMIORegion() const;
368
384
 
369
385
  /// A telemetry port which gets attached to a service port.
370
- class Telemetry : public ServicePort {
386
+ class Metric : public ServicePort {
371
387
  friend class TelemetryService;
372
- Telemetry(AppID id, const BundleType *type, PortMap channels);
388
+ Metric(AppID id, const BundleType *type, PortMap channels,
389
+ const TelemetryService *telemetryService,
390
+ std::optional<uint64_t> offset);
373
391
 
374
392
  public:
375
- static Telemetry *get(AppID id, BundleType *type, WriteChannelPort &get,
376
- ReadChannelPort &data);
377
-
378
393
  void connect();
379
394
  std::future<MessageData> read();
395
+ uint64_t readInt();
380
396
 
381
397
  virtual std::optional<std::string> toString() const override {
382
398
  const esi::Type *dataType =
@@ -386,16 +402,24 @@ public:
386
402
  }
387
403
 
388
404
  private:
389
- WriteChannelPort *get_req;
390
- ReadChannelPort *data;
405
+ const TelemetryService *telemetryService;
406
+ MMIO::MMIORegion *mmio;
407
+ std::optional<uint64_t> offset;
391
408
  };
392
409
 
393
- const std::map<AppIDPath, Telemetry *> &getTelemetryPorts() {
394
- return telemetryPorts;
410
+ std::map<AppIDPath, Metric *> getTelemetryPorts() {
411
+ std::map<AppIDPath, Metric *> ports;
412
+ getTelemetryPorts(ports);
413
+ return ports;
395
414
  }
415
+ void getTelemetryPorts(std::map<AppIDPath, Metric *> &ports);
396
416
 
397
417
  private:
398
- mutable std::map<AppIDPath, Telemetry *> telemetryPorts;
418
+ AppIDPath id;
419
+ mutable MMIO::MMIORegion *mmio;
420
+ std::map<AppIDPath, uint64_t> portAddressAssignments;
421
+ mutable std::map<AppIDPath, Metric *> telemetryPorts;
422
+ std::list<TelemetryService *> children;
399
423
  };
400
424
 
401
425
  /// Registry of services which can be instantiated directly by the Accelerator
@@ -26,6 +26,7 @@
26
26
  #include <vector>
27
27
 
28
28
  #include "esi/Common.h"
29
+ #include "esi/Values.h" // For BitVector / Int / UInt
29
30
 
30
31
  namespace esi {
31
32
 
@@ -39,20 +40,29 @@ public:
39
40
  ID getID() const { return id; }
40
41
  virtual std::ptrdiff_t getBitWidth() const { return -1; }
41
42
 
42
- /// Serialize an object to MessageData. The object should be passed as a
43
- /// std::any to provide type erasure. Returns a MessageData containing the
44
- /// serialized representation.
45
- virtual MessageData serialize(const std::any &obj) const {
43
+ /// Serialize an object to a MutableBitVector (LSB-first stream). The object
44
+ /// should be passed via std::any. Implementations append fields in the order
45
+ /// they are iterated (the first serialized field occupies the
46
+ /// least-significant bits of the result).
47
+ virtual MutableBitVector serialize(const std::any &obj) const {
46
48
  throw std::runtime_error("Serialization not implemented for type " + id);
47
49
  }
48
50
 
49
- /// Deserialize from a span of bytes to an object. Returns the deserialized
50
- /// object as a std::any and a span to the remaining bytes.
51
- virtual std::pair<std::any, std::span<const uint8_t>>
52
- deserialize(std::span<const uint8_t> data) const {
51
+ /// Deserialize from a BitVector stream (LSB-first). Implementations consume
52
+ /// bits from 'data' in-place (via logical right shifts) and return the
53
+ /// reconstructed value. Remaining bits stay in 'data'.
54
+ virtual std::any deserialize(BitVector &data) const {
53
55
  throw std::runtime_error("Deserialization not implemented for type " + id);
54
56
  }
55
57
 
58
+ // Deserialize from a MessageData buffer. Maps the MessageData onto a
59
+ // MutableBitVector, and proceeds with regular MutableBitVector
60
+ // deserialization.
61
+ std::any deserialize(const MessageData &data) const {
62
+ auto bv = MutableBitVector(std::vector<uint8_t>(data.getData()));
63
+ return deserialize(bv);
64
+ }
65
+
56
66
  /// Ensure that a std::any object is valid for this type. Throws
57
67
  /// std::runtime_error if the object is not valid.
58
68
  virtual void ensureValid(const std::any &obj) const {
@@ -70,6 +80,12 @@ public:
70
80
  }
71
81
  }
72
82
 
83
+ // Dump a textual representation of this type to the provided stream.
84
+ void dump(std::ostream &os);
85
+
86
+ // Return a textual representation of this type.
87
+ std::string toString();
88
+
73
89
  protected:
74
90
  ID id;
75
91
  };
@@ -101,14 +117,14 @@ protected:
101
117
  /// carry one values of one type.
102
118
  class ChannelType : public Type {
103
119
  public:
120
+ using Type::deserialize;
104
121
  ChannelType(const ID &id, const Type *inner) : Type(id), inner(inner) {}
105
122
  const Type *getInner() const { return inner; }
106
123
  std::ptrdiff_t getBitWidth() const override { return inner->getBitWidth(); };
107
124
 
108
125
  void ensureValid(const std::any &obj) const override;
109
- MessageData serialize(const std::any &obj) const override;
110
- std::pair<std::any, std::span<const uint8_t>>
111
- deserialize(std::span<const uint8_t> data) const override;
126
+ MutableBitVector serialize(const std::any &obj) const override;
127
+ std::any deserialize(BitVector &data) const override;
112
128
 
113
129
  private:
114
130
  const Type *inner;
@@ -117,14 +133,14 @@ private:
117
133
  /// The "void" type is a special type which can be used to represent no type.
118
134
  class VoidType : public Type {
119
135
  public:
136
+ using Type::deserialize;
120
137
  VoidType(const ID &id) : Type(id) {}
121
138
  // 'void' is 1 bit by convention.
122
139
  std::ptrdiff_t getBitWidth() const override { return 1; };
123
140
 
124
141
  void ensureValid(const std::any &obj) const override;
125
- MessageData serialize(const std::any &obj) const override;
126
- std::pair<std::any, std::span<const uint8_t>>
127
- deserialize(std::span<const uint8_t> data) const override;
142
+ MutableBitVector serialize(const std::any &obj) const override;
143
+ std::any deserialize(BitVector &data) const override;
128
144
  };
129
145
 
130
146
  /// The "any" type is a special type which can be used to represent any type, as
@@ -154,11 +170,11 @@ private:
154
170
  class BitsType : public BitVectorType {
155
171
  public:
156
172
  using BitVectorType::BitVectorType;
173
+ using Type::deserialize;
157
174
 
158
175
  void ensureValid(const std::any &obj) const override;
159
- MessageData serialize(const std::any &obj) const override;
160
- std::pair<std::any, std::span<const uint8_t>>
161
- deserialize(std::span<const uint8_t> data) const override;
176
+ MutableBitVector serialize(const std::any &obj) const override;
177
+ std::any deserialize(BitVector &data) const override;
162
178
  };
163
179
 
164
180
  /// Integers are bit vectors which may be signed or unsigned and are interpreted
@@ -172,28 +188,29 @@ public:
172
188
  class SIntType : public IntegerType {
173
189
  public:
174
190
  using IntegerType::IntegerType;
191
+ using Type::deserialize;
175
192
 
176
193
  void ensureValid(const std::any &obj) const override;
177
- MessageData serialize(const std::any &obj) const override;
178
- std::pair<std::any, std::span<const uint8_t>>
179
- deserialize(std::span<const uint8_t> data) const override;
194
+ MutableBitVector serialize(const std::any &obj) const override;
195
+ std::any deserialize(BitVector &data) const override;
180
196
  };
181
197
 
182
198
  /// Unsigned integer.
183
199
  class UIntType : public IntegerType {
184
200
  public:
185
201
  using IntegerType::IntegerType;
202
+ using Type::deserialize;
186
203
 
187
204
  void ensureValid(const std::any &obj) const override;
188
- MessageData serialize(const std::any &obj) const override;
189
- std::pair<std::any, std::span<const uint8_t>>
190
- deserialize(std::span<const uint8_t> data) const override;
205
+ MutableBitVector serialize(const std::any &obj) const override;
206
+ std::any deserialize(BitVector &data) const override;
191
207
  };
192
208
 
193
209
  /// Structs are an ordered collection of fields, each with a name and a type.
194
210
  class StructType : public Type {
195
211
  public:
196
212
  using FieldVector = std::vector<std::pair<std::string, const Type *>>;
213
+ using Type::deserialize;
197
214
 
198
215
  StructType(const ID &id, const FieldVector &fields, bool reverse = true)
199
216
  : Type(id), fields(fields), reverse(reverse) {}
@@ -211,9 +228,8 @@ public:
211
228
  }
212
229
 
213
230
  void ensureValid(const std::any &obj) const override;
214
- MessageData serialize(const std::any &obj) const override;
215
- std::pair<std::any, std::span<const uint8_t>>
216
- deserialize(std::span<const uint8_t> data) const override;
231
+ MutableBitVector serialize(const std::any &obj) const override;
232
+ std::any deserialize(BitVector &data) const override;
217
233
 
218
234
  // Returns whether this struct type should be reversed when
219
235
  // serializing/deserializing.
@@ -230,11 +246,14 @@ private:
230
246
  /// Arrays have a compile time specified (static) size and an element type.
231
247
  class ArrayType : public Type {
232
248
  public:
233
- ArrayType(const ID &id, const Type *elementType, uint64_t size)
234
- : Type(id), elementType(elementType), size(size) {}
249
+ ArrayType(const ID &id, const Type *elementType, uint64_t size,
250
+ bool reverse = true)
251
+ : Type(id), elementType(elementType), size(size), reverse(reverse) {}
252
+ using Type::deserialize;
235
253
 
236
254
  const Type *getElementType() const { return elementType; }
237
255
  uint64_t getSize() const { return size; }
256
+ bool isReverse() const { return reverse; }
238
257
  std::ptrdiff_t getBitWidth() const override {
239
258
  std::ptrdiff_t elementSize = elementType->getBitWidth();
240
259
  if (elementSize < 0)
@@ -243,13 +262,16 @@ public:
243
262
  }
244
263
 
245
264
  void ensureValid(const std::any &obj) const override;
246
- MessageData serialize(const std::any &obj) const override;
247
- std::pair<std::any, std::span<const uint8_t>>
248
- deserialize(std::span<const uint8_t> data) const override;
265
+ MutableBitVector serialize(const std::any &obj) const override;
266
+ std::any deserialize(BitVector &data) const override;
249
267
 
250
268
  private:
251
269
  const Type *elementType;
252
270
  uint64_t size;
271
+ // 'reverse' controls whether array elements are reversed during
272
+ // serialization/deserialization (to match SystemVerilog/Python ordering
273
+ // expectations).
274
+ bool reverse;
253
275
  };
254
276
 
255
277
  } // namespace esi