Add proc_rom pass.
[yosys.git] / kernel / mem.h
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #ifndef MEM_H
21 #define MEM_H
22
23 #include "kernel/yosys.h"
24 #include "kernel/ffinit.h"
25
26 YOSYS_NAMESPACE_BEGIN
27
28 struct MemRd : RTLIL::AttrObject {
29 bool removed;
30 Cell *cell;
31 int wide_log2;
32 bool clk_enable, clk_polarity, ce_over_srst;
33 Const arst_value, srst_value, init_value;
34 // One bit for every write port, true iff simultanous read on this
35 // port and write on the other port will bypass the written data
36 // to this port's output (default behavior is to read old value).
37 // Can only be set for write ports that have the same clock domain.
38 std::vector<bool> transparency_mask;
39 // One bit for every write port, true iff simultanous read on this
40 // port and write on the other port will return an all-X (don't care)
41 // value. Mutually exclusive with transparency_mask.
42 // Can only be set for write ports that have the same clock domain.
43 // For optimization purposes, this will also be set if we can
44 // determine that the two ports can never be active simultanously
45 // (making the above vacuously true).
46 std::vector<bool> collision_x_mask;
47 SigSpec clk, en, arst, srst, addr, data;
48
49 MemRd() : removed(false), cell(nullptr), wide_log2(0), clk_enable(false), clk_polarity(true), ce_over_srst(false), clk(State::Sx), en(State::S1), arst(State::S0), srst(State::S0) {}
50
51 // Returns the address of given subword index accessed by this port.
52 SigSpec sub_addr(int sub) {
53 SigSpec res = addr;
54 for (int i = 0; i < wide_log2; i++)
55 res[i] = State(sub >> i & 1);
56 return res;
57 }
58 };
59
60 struct MemWr : RTLIL::AttrObject {
61 bool removed;
62 Cell *cell;
63 int wide_log2;
64 bool clk_enable, clk_polarity;
65 std::vector<bool> priority_mask;
66 SigSpec clk, en, addr, data;
67
68 MemWr() : removed(false), cell(nullptr) {}
69
70 // Returns the address of given subword index accessed by this port.
71 SigSpec sub_addr(int sub) {
72 SigSpec res = addr;
73 for (int i = 0; i < wide_log2; i++)
74 res[i] = State(sub >> i & 1);
75 return res;
76 }
77
78 std::pair<SigSpec, std::vector<int>> compress_en();
79 SigSpec decompress_en(const std::vector<int> &swizzle, SigSpec sig);
80 };
81
82 struct MemInit : RTLIL::AttrObject {
83 bool removed;
84 Cell *cell;
85 Const addr;
86 Const data;
87 Const en;
88 MemInit() : removed(false), cell(nullptr) {}
89 };
90
91 struct Mem : RTLIL::AttrObject {
92 Module *module;
93 IdString memid;
94 bool packed;
95 RTLIL::Memory *mem;
96 Cell *cell;
97 int width, start_offset, size;
98 std::vector<MemInit> inits;
99 std::vector<MemRd> rd_ports;
100 std::vector<MemWr> wr_ports;
101
102 // Removes this memory from the module. The data in helper structures
103 // is unaffected except for the cell/mem fields.
104 void remove();
105
106 // Commits all changes in helper structures into the module — ports and
107 // inits marked as removed are actually removed, new ports/inits create
108 // new cells, modified port/inits are commited into their existing
109 // cells. Note that this reindexes the ports and inits array (actually
110 // removing the ports/inits marked as removed).
111 void emit();
112
113 // Marks all inits as removed.
114 void clear_inits();
115
116 // Coalesces inits: whenever two inits have overlapping or touching
117 // address ranges, they are combined into one, with the higher-priority
118 // one's data overwriting the other. Running this results in
119 // an inits list equivalent to the original, in which all entries
120 // cover disjoint (and non-touching) address ranges, and all enable
121 // masks are all-1.
122 void coalesce_inits();
123
124 // Checks consistency of this memory and all its ports/inits, using
125 // log_assert.
126 void check();
127
128 // Gathers all initialization data into a single big const covering
129 // the whole memory. For all non-initialized bits, Sx will be returned.
130 Const get_init_data() const;
131
132 // Constructs and returns the helper structures for all memories
133 // in a module.
134 static std::vector<Mem> get_all_memories(Module *module);
135
136 // Constructs and returns the helper structures for all selected
137 // memories in a module.
138 static std::vector<Mem> get_selected_memories(Module *module);
139
140 // Converts a synchronous read port into an asynchronous one by
141 // extracting the data (or, in some rare cases, address) register
142 // into a separate cell, together with any soft-transparency
143 // logic necessary to preserve its semantics. Returns the created
144 // register cell, if any. Note that in some rare cases this function
145 // may succeed and perform a conversion without creating a new
146 // register — a nullptr result doesn't imply nothing was done.
147 Cell *extract_rdff(int idx, FfInitVals *initvals);
148
149 // Splits all wide ports in this memory into equivalent narrow ones.
150 // This function performs no modifications at all to the actual
151 // netlist unless and until emit() is called.
152 void narrow();
153
154 // If write port idx2 currently has priority over write port idx1,
155 // inserts extra logic on idx1's enable signal to disable writes
156 // when idx2 is writing to the same address, then removes the priority
157 // from the priority mask. If there is a memory port that is
158 // transparent with idx1, but not with idx2, that port is converted
159 // to use soft transparency logic.
160 void emulate_priority(int idx1, int idx2, FfInitVals *initvals);
161
162 // Creates soft-transparency logic on read port ridx, bypassing the
163 // data from write port widx. Should only be called when ridx is
164 // transparent wrt widx in the first place. Once we're done, the
165 // transparency_mask bit will be cleared, and the collision_x_mask
166 // bit will be set instead (since whatever value is read will be
167 // replaced by the soft transparency logic).
168 void emulate_transparency(int widx, int ridx, FfInitVals *initvals);
169
170 // Prepares for merging write port idx2 into idx1 (where idx1 < idx2).
171 // Specifically, takes care of priority masks: any priority relations
172 // that idx2 had are replicated onto idx1, unless they conflict with
173 // priorities already present on idx1, in which case emulate_priority
174 // is called. Likewise, ensures transparency and undefined collision
175 // masks of all read ports have the same values for both ports,
176 // calling emulate_transparency if necessary.
177 void prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals);
178
179 // Prepares for merging read port idx2 into idx1.
180 // Specifically, makes sure the transparency and undefined collision
181 // masks of both ports are equal, by changing undefined behavior
182 // of one port to the other's defined behavior, or by calling
183 // emulate_transparency if necessary.
184 void prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals);
185
186 // Prepares the memory for widening a port to a given width. This
187 // involves ensuring that start_offset and size are aligned to the
188 // target width.
189 void widen_prep(int wide_log2);
190
191 // Widens a write port up to a given width. The newly port is
192 // equivalent to the original, made by replicating enable/data bits
193 // and masking enable bits with decoders on the low part of the
194 // original address.
195 void widen_wr_port(int idx, int wide_log2);
196
197 // Emulates a sync read port's enable functionality in soft logic,
198 // changing the actual read port's enable to be always-on.
199 void emulate_rden(int idx, FfInitVals *initvals);
200
201 // Emulates a sync read port's initial/reset value functionality in
202 // soft logic, removing it from the actual read port.
203 void emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals);
204
205 // Given a read port with ce_over_srst set, converts it to a port
206 // with ce_over_srst unset without changing its behavior by adding
207 // emulation logic.
208 void emulate_rd_ce_over_srst(int idx);
209
210 // Given a read port with ce_over_srst unset, converts it to a port
211 // with ce_over_srst set without changing its behavior by adding
212 // emulation logic.
213 void emulate_rd_srst_over_ce(int idx);
214
215 // Returns true iff emulate_read_first makes sense to call.
216 bool emulate_read_first_ok();
217
218 // Emulates all read-first read-write port relationships in terms of
219 // all-transparent ports, by delaying all write ports by one cycle.
220 // This can only be used when all read ports and all write ports are
221 // in the same clock domain.
222 void emulate_read_first(FfInitVals *initvals);
223
224 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) {}
225 };
226
227 YOSYS_NAMESPACE_END
228
229 #endif