aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/mem.h
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/mem.h')
-rw-r--r--kernel/mem.h185
1 files changed, 168 insertions, 17 deletions
diff --git a/kernel/mem.h b/kernel/mem.h
index 6d727e71d..ae87b1285 100644
--- a/kernel/mem.h
+++ b/kernel/mem.h
@@ -21,38 +21,76 @@
#define MEM_H
#include "kernel/yosys.h"
+#include "kernel/ffinit.h"
YOSYS_NAMESPACE_BEGIN
-struct MemRd {
- dict<IdString, Const> attributes;
+struct MemRd : RTLIL::AttrObject {
+ bool removed;
Cell *cell;
- bool clk_enable, clk_polarity;
- bool transparent;
- SigSpec clk, en, addr, data;
- MemRd() : cell(nullptr) {}
+ int wide_log2;
+ bool clk_enable, clk_polarity, ce_over_srst;
+ Const arst_value, srst_value, init_value;
+ // One bit for every write port, true iff simultanous read on this
+ // port and write on the other port will bypass the written data
+ // to this port's output (default behavior is to read old value).
+ // Can only be set for write ports that have the same clock domain.
+ std::vector<bool> transparency_mask;
+ // One bit for every write port, true iff simultanous read on this
+ // port and write on the other port will return an all-X (don't care)
+ // value. Mutually exclusive with transparency_mask.
+ // Can only be set for write ports that have the same clock domain.
+ // For optimization purposes, this will also be set if we can
+ // determine that the two ports can never be active simultanously
+ // (making the above vacuously true).
+ std::vector<bool> collision_x_mask;
+ SigSpec clk, en, arst, srst, addr, data;
+
+ MemRd() : removed(false), cell(nullptr) {}
+
+ // Returns the address of given subword index accessed by this port.
+ SigSpec sub_addr(int sub) {
+ SigSpec res = addr;
+ for (int i = 0; i < wide_log2; i++)
+ res[i] = State(sub >> i & 1);
+ return res;
+ }
};
-struct MemWr {
- dict<IdString, Const> attributes;
+struct MemWr : RTLIL::AttrObject {
+ bool removed;
Cell *cell;
+ int wide_log2;
bool clk_enable, clk_polarity;
+ std::vector<bool> priority_mask;
SigSpec clk, en, addr, data;
- MemWr() : cell(nullptr) {}
+
+ MemWr() : removed(false), cell(nullptr) {}
+
+ // Returns the address of given subword index accessed by this port.
+ SigSpec sub_addr(int sub) {
+ SigSpec res = addr;
+ for (int i = 0; i < wide_log2; i++)
+ res[i] = State(sub >> i & 1);
+ return res;
+ }
+
+ std::pair<SigSpec, std::vector<int>> compress_en();
+ SigSpec decompress_en(const std::vector<int> &swizzle, SigSpec sig);
};
-struct MemInit {
- dict<IdString, Const> attributes;
+struct MemInit : RTLIL::AttrObject {
+ bool removed;
Cell *cell;
Const addr;
Const data;
- MemInit() : cell(nullptr) {}
+ Const en;
+ MemInit() : removed(false), cell(nullptr) {}
};
-struct Mem {
+struct Mem : RTLIL::AttrObject {
Module *module;
IdString memid;
- dict<IdString, Const> attributes;
bool packed;
RTLIL::Memory *mem;
Cell *cell;
@@ -61,15 +99,128 @@ struct Mem {
std::vector<MemRd> rd_ports;
std::vector<MemWr> wr_ports;
+ // Removes this memory from the module. The data in helper structures
+ // is unaffected except for the cell/mem fields.
void remove();
+
+ // Commits all changes in helper structures into the module — ports and
+ // inits marked as removed are actually removed, new ports/inits create
+ // new cells, modified port/inits are commited into their existing
+ // cells. Note that this reindexes the ports and inits array (actually
+ // removing the ports/inits marked as removed).
void emit();
- void remove_wr_port(int idx);
- void remove_rd_port(int idx);
+
+ // Marks all inits as removed.
void clear_inits();
+
+ // Coalesces inits: whenever two inits have overlapping or touching
+ // address ranges, they are combined into one, with the higher-priority
+ // one's data overwriting the other. Running this results in
+ // an inits list equivalent to the original, in which all entries
+ // cover disjoint (and non-touching) address ranges, and all enable
+ // masks are all-1.
+ void coalesce_inits();
+
+ // Checks consistency of this memory and all its ports/inits, using
+ // log_assert.
+ void check();
+
+ // Gathers all initialization data into a single big const covering
+ // the whole memory. For all non-initialized bits, Sx will be returned.
Const get_init_data() const;
+
+ // Constructs and returns the helper structures for all memories
+ // in a module.
static std::vector<Mem> get_all_memories(Module *module);
+
+ // Constructs and returns the helper structures for all selected
+ // memories in a module.
static std::vector<Mem> get_selected_memories(Module *module);
- Cell *extract_rdff(int idx);
+
+ // Converts a synchronous read port into an asynchronous one by
+ // extracting the data (or, in some rare cases, address) register
+ // into a separate cell, together with any soft-transparency
+ // logic necessary to preserve its semantics. Returns the created
+ // register cell, if any. Note that in some rare cases this function
+ // may succeed and perform a conversion without creating a new
+ // register — a nullptr result doesn't imply nothing was done.
+ Cell *extract_rdff(int idx, FfInitVals *initvals);
+
+ // Splits all wide ports in this memory into equivalent narrow ones.
+ // This function performs no modifications at all to the actual
+ // netlist unless and until emit() is called.
+ void narrow();
+
+ // If write port idx2 currently has priority over write port idx1,
+ // inserts extra logic on idx1's enable signal to disable writes
+ // when idx2 is writing to the same address, then removes the priority
+ // from the priority mask. If there is a memory port that is
+ // transparent with idx1, but not with idx2, that port is converted
+ // to use soft transparency logic.
+ void emulate_priority(int idx1, int idx2, FfInitVals *initvals);
+
+ // Creates soft-transparency logic on read port ridx, bypassing the
+ // data from write port widx. Should only be called when ridx is
+ // transparent wrt widx in the first place. Once we're done, the
+ // transparency_mask bit will be cleared, and the collision_x_mask
+ // bit will be set instead (since whatever value is read will be
+ // replaced by the soft transparency logic).
+ void emulate_transparency(int widx, int ridx, FfInitVals *initvals);
+
+ // Prepares for merging write port idx2 into idx1 (where idx1 < idx2).
+ // Specifically, takes care of priority masks: any priority relations
+ // that idx2 had are replicated onto idx1, unless they conflict with
+ // priorities already present on idx1, in which case emulate_priority
+ // is called. Likewise, ensures transparency and undefined collision
+ // masks of all read ports have the same values for both ports,
+ // calling emulate_transparency if necessary.
+ void prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals);
+
+ // Prepares for merging read port idx2 into idx1.
+ // Specifically, makes sure the transparency and undefined collision
+ // masks of both ports are equal, by changing undefined behavior
+ // of one port to the other's defined behavior, or by calling
+ // emulate_transparency if necessary.
+ void prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals);
+
+ // Prepares the memory for widening a port to a given width. This
+ // involves ensuring that start_offset and size are aligned to the
+ // target width.
+ void widen_prep(int wide_log2);
+
+ // Widens a write port up to a given width. The newly port is
+ // equivalent to the original, made by replicating enable/data bits
+ // and masking enable bits with decoders on the low part of the
+ // original address.
+ void widen_wr_port(int idx, int wide_log2);
+
+ // Emulates a sync read port's enable functionality in soft logic,
+ // changing the actual read port's enable to be always-on.
+ void emulate_rden(int idx, FfInitVals *initvals);
+
+ // Emulates a sync read port's initial/reset value functionality in
+ // soft logic, removing it from the actual read port.
+ void emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals);
+
+ // Given a read port with ce_over_srst set, converts it to a port
+ // with ce_over_srst unset without changing its behavior by adding
+ // emulation logic.
+ void emulate_rd_ce_over_srst(int idx);
+
+ // Given a read port with ce_over_srst unset, converts it to a port
+ // with ce_over_srst set without changing its behavior by adding
+ // emulation logic.
+ void emulate_rd_srst_over_ce(int idx);
+
+ // Returns true iff emulate_read_first makes sense to call.
+ bool emulate_read_first_ok();
+
+ // Emulates all read-first read-write port relationships in terms of
+ // all-transparent ports, by delaying all write ports by one cycle.
+ // This can only be used when all read ports and all write ports are
+ // in the same clock domain.
+ void emulate_read_first(FfInitVals *initvals);
+
Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
};