| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| #ifndef OPENTITAN_HW_DV_VERILATOR_CPP_DPI_MEMUTIL_H_ |
| #define OPENTITAN_HW_DV_VERILATOR_CPP_DPI_MEMUTIL_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <svdpi.h> |
| #include <vector> |
| |
| #include "mem_area.h" |
| #include "ranged_map.h" |
| |
| // Forward declaration for the Elf type from libelf. |
| struct Elf; |
| |
| enum MemImageType { |
| kMemImageUnknown = 0, |
| kMemImageElf, |
| kMemImageVmem, |
| }; |
| |
| // Staged data for a given memory area. |
| // |
| // This is represented as an ordered list of disjoint segments (as loaded from |
| // an ELF file). |
| // |
| // Once it is nonempty, the class maintains the invariant that min_addr_ / |
| // max_addr_ is the smallest / largest byte offset with valid data. |
| class StagedMem { |
| public: |
| StagedMem() : min_addr_(~(uint32_t)0), max_addr_(0) {} |
| |
| // Add a segment to the tracked memory |
| void AddSegment(uint32_t offset, std::vector<uint8_t> &&seg); |
| |
| // Glob together the tracked segments, interspersing them with |
| // zeros, and return as a single flat array. |
| std::vector<uint8_t> GetFlat() const; |
| |
| typedef RangedMap<uint32_t, std::vector<uint8_t>> SegMap; |
| |
| std::pair<uint32_t, uint32_t> GetBounds() const { |
| return std::make_pair(min_addr_, max_addr_); |
| } |
| const SegMap &GetSegs() const { return segs_; } |
| |
| private: |
| uint32_t min_addr_, max_addr_; |
| SegMap segs_; |
| }; |
| |
| /** |
| * Provide various memory loading utilities for verilog simulations |
| * |
| * These utilities require the corresponding DPI functions: |
| * simutil_memload() |
| * simutil_set_mem() |
| * to be defined somewhere as SystemVerilog functions. |
| */ |
| class DpiMemUtil { |
| public: |
| virtual ~DpiMemUtil() {} |
| |
| /** |
| * Register a memory as instantiated by generic ram |
| * |
| * The |name| must be a unique identifier. The constructor will |
| * throw a std::runtime_error if |name| is already used. |
| * |
| * |base| gives the base address of the memory in a logical address |
| * space that corresponds to LMAs in an ELF file. If this overlaps |
| * with some other registered memory, the constructor throws a |
| * std::runtime_error. |
| * |
| * The |mem_area| argument describes the memory area to be registered and |
| * must not be null. This function does not take ownership of the object, |
| * which must survive at least as long as the DpiMemutil object. |
| * |
| * Memories must be registered before command arguments are parsed by |
| * ParseCommandArgs() in order for them to be known. |
| */ |
| void RegisterMemoryArea(const std::string &name, uint32_t base, |
| const MemArea *mem_area); |
| |
| /** |
| * Guess the type of the file at |path|. |
| * |
| * If |type| is non-null, it is the name of an image type and will be used. |
| * Otherwise, the check is based on |path|. If |type| is not a valid name or |
| * if the function can't guess from the path, throws a std::runtime_error |
| * with a message about what went wrong. |
| * |
| * Never returns kMemImageUnknown. |
| */ |
| static MemImageType GetMemImageType(const std::string &path, |
| const char *type); |
| |
| /** |
| * Print a list of all registered memory regions |
| * |
| * @see RegisterMemoryArea() |
| */ |
| void PrintMemRegions() const; |
| |
| /** |
| * Load the file at filepath into the named memory. If type is |
| * kMemImageUnknown, the file type is determined from the path. |
| */ |
| void LoadFileToNamedMem(bool verbose, const std::string &name, |
| const std::string &filepath, MemImageType type); |
| |
| /** |
| * Load an ELF file, placing segments in memories by LMA. |
| * |
| * Replaces any data currently in the staging area. |
| */ |
| void LoadElfToMemories(bool verbose, const std::string &filepath); |
| |
| /** |
| * Load an ELF file into a staging area in this object, which can then be |
| * accessed with GetMemoryData(). |
| * |
| * If the load fails, raises a std::exception with information about what |
| * happened. |
| */ |
| void StageElf(bool verbose, const std::string &path); |
| |
| /** |
| * Get the contents of the staging area by memory name |
| */ |
| const StagedMem &GetMemoryData(const std::string &mem_name) const; |
| |
| protected: |
| /** |
| * A hook for subclasses to do extra computations with loaded ELF data. This |
| * runs as part of StageElf: after loading the ELF file, but before reading |
| * in the segments. |
| */ |
| virtual void OnElfLoaded(Elf *elf_file) {} |
| |
| private: |
| // Memory area registry. The maps give indices pointing into the vectors |
| // (which all have the same number of elements). Note that mem_areas_ does |
| // not own the objects that it points to. |
| std::vector<const MemArea *> mem_areas_; |
| std::vector<uint32_t> base_addrs_; |
| std::vector<std::string> names_; |
| |
| std::map<std::string, size_t> name_to_mem_; |
| RangedMap<uint32_t, size_t> addr_to_mem_; |
| |
| // Staging area, loaded by StageElf. The map is keyed by names of memories |
| // stored in name_to_mem_. We also ensure that every segment in a StagedMem |
| // for a memory starts at an address that's aligned for the word width of |
| // that memory. Note: we don't also check segments' lengths are aligned. |
| std::map<std::string, StagedMem> staging_area_; |
| const StagedMem empty_; |
| |
| /** |
| * Find the index of a memory area containing the given segment's addresses. |
| * Raises a std::exception if none is found. |
| */ |
| size_t GetRegionForSegment(const std::string &path, int seg_idx, uint32_t lma, |
| uint32_t mem_sz) const; |
| }; |
| |
| #endif // OPENTITAN_HW_DV_VERILATOR_CPP_DPI_MEMUTIL_H_ |