prjunnamed_siliconblue/
memory.rs

1use prjunnamed_memory::{MemoryExt, MemorySwizzle};
2use prjunnamed_netlist::{
3    Cell, Const, ControlNet, Design, Memory, MemoryPortRelation, Net, Target, TargetCell, Trit, Value,
4};
5
6use crate::SiliconBlueTarget;
7
8#[derive(Clone, Debug)]
9struct BramLowering {
10    read_port_swizzles: Vec<MemorySwizzle>,
11}
12
13const OPT_FOR_AREA: bool = false;
14
15impl SiliconBlueTarget {
16    fn find_bram_lowering(&self, memory: &Memory) -> Option<BramLowering> {
17        // A memory can be lowered to SiliconBlue BRAM iff it satisfies two conditions:
18        //
19        // - at most one write port
20        // - all read ports are synchronous
21        if memory.write_ports.len() > 1 {
22            return None;
23        }
24        if memory.read_ports.iter().any(|port| port.flip_flop.is_none()) {
25            return None;
26        }
27        let write_port = memory.write_ports.get(0);
28        let mut read_port_swizzles = vec![];
29        for (read_port_index, read_port) in memory.read_ports.iter().enumerate() {
30            let mut cand_swizzles = vec![];
31            for base_mode in 0..3 {
32                if base_mode != 0 && !self.is_ice40() {
33                    continue;
34                }
35                let mut soft_addr_bits_mask = 0;
36                let mut write_wide_log2 = vec![];
37                let data_swizzle = if let Some(write_port) = write_port {
38                    let wide_log2 = write_port.wide_log2(memory);
39                    if wide_log2 > base_mode {
40                        for bit in base_mode..wide_log2 {
41                            soft_addr_bits_mask |= 1 << bit;
42                        }
43                    }
44                    let actual_wide_log2 = wide_log2.min(base_mode);
45                    write_wide_log2.push(actual_wide_log2);
46                    memory.make_data_swizzle(&[if base_mode == actual_wide_log2 {
47                        1
48                    } else {
49                        16 >> base_mode << actual_wide_log2
50                    }])
51                } else {
52                    memory.make_data_swizzle(&[])
53                };
54                let read_wide_log2 = read_port.wide_log2(memory);
55                if read_wide_log2 > base_mode {
56                    for bit in base_mode..read_wide_log2 {
57                        soft_addr_bits_mask |= 1 << bit;
58                    }
59                }
60                let read_wide_log2 = vec![read_wide_log2.min(base_mode)];
61                let mut swizzle = MemorySwizzle {
62                    data_swizzle,
63                    soft_addr_bits_mask,
64                    write_wide_log2,
65                    read_wide_log2,
66                    hard_addr_bits: 8 + base_mode,
67                    data_width_unit: 16 >> base_mode,
68                };
69                cand_swizzles.push(swizzle.clone());
70                if write_port.is_some() && swizzle.write_wide_log2[0] != base_mode {
71                    swizzle.write_wide_log2[0] = base_mode;
72                    swizzle.data_swizzle = memory.make_data_swizzle(&[1]);
73                    cand_swizzles.push(swizzle);
74                }
75            }
76            let best_swizzle = if OPT_FOR_AREA {
77                cand_swizzles
78                    .into_iter()
79                    .min_by_key(|swizzle| {
80                        let num_brams = memory.swizzle_depths(swizzle).len();
81                        let (write_mux_bits, read_mux_bits) = memory.swizzle_mux_bits(&[read_port_index], swizzle);
82                        let write_mux_bits = write_mux_bits.get(0).copied().unwrap_or(0);
83                        let read_mux_bits = read_mux_bits[0];
84                        (num_brams, read_mux_bits, write_mux_bits)
85                    })
86                    .unwrap()
87            } else {
88                cand_swizzles
89                    .into_iter()
90                    .min_by_key(|swizzle| {
91                        let num_brams = memory.swizzle_depths(swizzle).len();
92                        let (write_mux_bits, read_mux_bits) = memory.swizzle_mux_bits(&[read_port_index], swizzle);
93                        let write_mux_bits = write_mux_bits.get(0).copied().unwrap_or(0);
94                        let read_mux_bits = read_mux_bits[0];
95                        (read_mux_bits, num_brams, write_mux_bits)
96                    })
97                    .unwrap()
98            };
99            read_port_swizzles.push(best_swizzle);
100        }
101        Some(BramLowering { read_port_swizzles })
102    }
103
104    fn swizzle_bram_addr(&self, port_width: usize, addr: &Value) -> Value {
105        assert!(addr.len() >= 8 && addr.len() <= 11);
106        assert_eq!(port_width, 16 >> (addr.len() - 8));
107        match addr.len() {
108            8 => addr.zext(11),
109            9 => addr.slice(1..).concat(addr[0]).zext(11),
110            10 => addr.slice(2..).concat(addr[1]).concat(addr[0]).zext(11),
111            11 => addr.slice(3..).concat(addr[2]).concat(addr[1]).concat(addr[0]),
112            _ => unreachable!(),
113        }
114    }
115
116    fn lower_single_bram(&self, design: &Design, memory: &Memory, output: &Value) {
117        const SWIZZLE16: [usize; 16] = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
118        let prototype = self.prototype("SB_RAM40_4K").unwrap();
119        let mut target_cell = TargetCell::new("SB_RAM40_4K", prototype);
120
121        assert!(memory.write_ports.len() <= 1);
122        assert_eq!(memory.read_ports.len(), 1);
123        assert!(memory.depth * memory.width <= 0x1000);
124
125        if let Some(write_port) = memory.write_ports.get(0) {
126            prototype.apply_param(&mut target_cell, "IS_WCLK_INVERTED", write_port.clock.is_negative());
127            prototype.apply_input(&mut target_cell, "WCLK", write_port.clock.net());
128            prototype.apply_input(&mut target_cell, "WE", Net::ONE);
129            prototype.apply_input(
130                &mut target_cell,
131                "WADDR",
132                self.swizzle_bram_addr(write_port.data.len(), &write_port.addr),
133            );
134            prototype.apply_param(&mut target_cell, "WRITE_MODE", (write_port.addr.len() - 8) as i64);
135            if write_port.data.len() != 16 {
136                prototype.apply_input(&mut target_cell, "WCLKE", write_port.mask[0]);
137                for bit in &write_port.mask {
138                    if bit != Net::UNDEF {
139                        assert_eq!(bit, write_port.mask[0]);
140                    }
141                }
142            } else {
143                prototype.apply_input(&mut target_cell, "WCLKE", Net::ONE);
144            }
145            match write_port.data.len() {
146                16 => {
147                    prototype.apply_input(
148                        &mut target_cell,
149                        "WDATA",
150                        Value::from_iter(SWIZZLE16.into_iter().map(|index| write_port.data[index])),
151                    );
152                    prototype.apply_input(
153                        &mut target_cell,
154                        "MASK",
155                        design.add_not(Value::from_iter(SWIZZLE16.into_iter().map(|index| write_port.mask[index]))),
156                    );
157                }
158                8 => {
159                    prototype.apply_input(
160                        &mut target_cell,
161                        "WDATA",
162                        Value::from_iter(SWIZZLE16.into_iter().map(|index| {
163                            if index < 8 {
164                                write_port.data[index]
165                            } else {
166                                Net::UNDEF
167                            }
168                        })),
169                    );
170                }
171                4 => {
172                    prototype.apply_input(
173                        &mut target_cell,
174                        "WDATA",
175                        Value::from_iter(SWIZZLE16.into_iter().map(|index| {
176                            if index >= 8 && index < 12 {
177                                write_port.data[index - 8]
178                            } else {
179                                Net::UNDEF
180                            }
181                        })),
182                    );
183                }
184                2 => {
185                    prototype.apply_input(
186                        &mut target_cell,
187                        "WDATA",
188                        Value::from_iter(SWIZZLE16.into_iter().map(|index| {
189                            if index >= 12 && index < 14 {
190                                write_port.data[index - 12]
191                            } else {
192                                Net::UNDEF
193                            }
194                        })),
195                    );
196                }
197                _ => unreachable!(),
198            }
199        } else {
200            prototype.apply_input(&mut target_cell, "WCLK", Net::ZERO);
201        }
202
203        let read_port = &memory.read_ports[0];
204        let read_ff = read_port.flip_flop.as_ref().unwrap();
205        prototype.apply_param(&mut target_cell, "IS_RCLK_INVERTED", read_ff.clock.is_negative());
206        prototype.apply_input(&mut target_cell, "RCLK", read_ff.clock.net());
207        assert!(read_ff.enable.is_positive());
208        prototype.apply_input(&mut target_cell, "RCLKE", read_ff.enable.net());
209        prototype.apply_input(&mut target_cell, "RE", Net::ONE);
210        assert!(!read_ff.has_reset());
211        assert!(!read_ff.has_clear());
212        assert!(!read_ff.has_init_value());
213        for &relation in &read_ff.relations {
214            assert_eq!(relation, MemoryPortRelation::Undefined);
215        }
216        prototype.apply_input(&mut target_cell, "RADDR", self.swizzle_bram_addr(read_port.data_len, &read_port.addr));
217        prototype.apply_param(&mut target_cell, "READ_MODE", (read_port.addr.len() - 8) as i64);
218
219        let init_value = Const::from_iter((0..0x1000).map(|index| {
220            let swz_index = (index & 0xff0) | SWIZZLE16[index & 0xf];
221            if swz_index < memory.init_value.len() {
222                memory.init_value[swz_index]
223            } else {
224                Trit::Undef
225            }
226        }));
227        prototype.apply_param(&mut target_cell, "INIT", init_value);
228
229        let target_cell_output = design.add_target(target_cell);
230        let rdata = prototype.extract_output(&target_cell_output, "RDATA");
231        match memory.read_ports[0].data_len {
232            16 => {
233                for (rdata_bit, index) in SWIZZLE16.into_iter().enumerate() {
234                    design.replace_net(output[index], rdata[rdata_bit]);
235                }
236            }
237            8 => {
238                for (rdata_bit, index) in SWIZZLE16.into_iter().enumerate() {
239                    if index < 8 {
240                        design.replace_net(output[index], rdata[rdata_bit]);
241                    }
242                }
243            }
244            4 => {
245                for (rdata_bit, index) in SWIZZLE16.into_iter().enumerate() {
246                    if index >= 8 && index < 12 {
247                        design.replace_net(output[index - 8], rdata[rdata_bit]);
248                    }
249                }
250            }
251            2 => {
252                for (rdata_bit, index) in SWIZZLE16.into_iter().enumerate() {
253                    if index >= 12 && index < 14 {
254                        design.replace_net(output[index - 12], rdata[rdata_bit]);
255                    }
256                }
257            }
258            _ => unreachable!(),
259        }
260    }
261
262    fn lower_to_bram(&self, design: &Design, memory: &Memory, output: &Value, lowering: BramLowering) {
263        for (read_port_index, swizzle) in lowering.read_port_swizzles.iter().enumerate() {
264            let (mut port_memory, mut port_output) = memory.extract_read_ports(&[read_port_index], output);
265            port_memory.unmap_read_init_reset_transparency(design, 0, true, &mut port_output);
266            let read_ff = port_memory.read_ports[0].flip_flop.as_mut().unwrap();
267            if let ControlNet::Neg(en) = read_ff.enable {
268                read_ff.enable = ControlNet::Pos(design.add_not1(en));
269            }
270            port_memory.emulate_read_before_write(design);
271            for (final_memory, final_output) in port_memory.swizzle(design, &port_output, swizzle) {
272                self.lower_single_bram(design, &final_memory, &final_output);
273            }
274        }
275    }
276
277    pub fn lower_memories(&self, design: &mut Design) {
278        for cell_ref in design.iter_cells() {
279            let Cell::Memory(memory) = &*cell_ref.get() else { continue };
280            let _guard = design.use_metadata_from(&[cell_ref]);
281            let output = cell_ref.output();
282            let bram_lowering = self.find_bram_lowering(memory);
283            let fallback_ok = memory.can_lower_fallback();
284            let fallback_reasonable =
285                fallback_ok && if memory.write_ports.len() != 0 { memory.depth <= 1 } else { memory.depth <= 5 };
286            cell_ref.unalive();
287            if bram_lowering.is_some() && !fallback_reasonable {
288                self.lower_to_bram(design, memory, &output, bram_lowering.unwrap());
289            } else {
290                memory.clone().lower_fallback(design, &output);
291            }
292        }
293        design.compact();
294    }
295}