rust_hdl_sim/sdr_sdram/
bank.rs

1use rust_hdl_core::prelude::*;
2use rust_hdl_widgets::prelude::*;
3
4#[derive(Copy, Clone, PartialEq, Debug, LogicState)]
5pub enum BankState {
6    Boot,
7    Idle,
8    Active,
9    Reading,
10    Precharging,
11    Writing,
12    Error,
13    Autorefreshing,
14    WriteRecovery,
15}
16
17// Bank state machine - a bank is simulated using BRAM.
18// Tbis can be generalized later.  For now, we set the
19// number of rows to 256, and the number of columns to 32
20// That yields 8 row addresses, and 5 column addresses, for
21// a total of 13 address bits.
22#[derive(LogicBlock)]
23pub struct MemoryBank<const R: usize, const C: usize, const A: usize, const D: usize> {
24    // Constraint - A = R + C
25    pub clock: Signal<In, Clock>,
26    pub cas_delay: Signal<In, Bits<3>>,
27    pub write_burst: Signal<In, Bit>,
28    pub address: Signal<In, Bits<13>>,
29    pub burst_len: Signal<In, Bits<4>>,
30    pub cmd: Signal<In, SDRAMCommand>,
31    pub error: Signal<Out, Bit>,
32    pub busy: Signal<Out, Bit>,
33    pub write_data: Signal<In, Bits<D>>,
34    pub read_data: Signal<Out, Bits<D>>,
35    pub read_valid: Signal<Out, Bit>,
36    pub select: Signal<In, Bit>,
37    delay_line: DelayLine<Bits<D>, 7, 3>,
38    read_delay_line: DelayLine<Bit, 7, 3>,
39    refresh_counter: DFF<Bits<32>>,
40    refresh_active: DFF<Bit>,
41    mem: RAM<Bits<D>, A>,
42    write_reg: DFF<Bits<D>>,
43    state: DFF<BankState>,
44    auto_precharge: DFF<Bit>,
45    active_row: DFF<Bits<R>>,
46    burst_counter: DFF<Bits<4>>,
47    active_col: DFF<Bits<C>>,
48    delay_counter: DFF<Bits<32>>,
49    t_activate: DFF<Bits<32>>,
50    t_ras: Constant<Bits<32>>, // Min time from activate to precharge
51    t_rc: Constant<Bits<32>>,  // Min time from active to activate
52    t_rcd: Constant<Bits<32>>, // Min time from active to read/write
53    t_rp: Constant<Bits<32>>,  // Precharge command time
54    t_wr: Constant<Bits<32>>,  // Write recovery time
55    t_refresh_max: Constant<Bits<32>>,
56    t_rfc: Constant<Bits<32>>,
57    row_shift: Constant<Bits<A>>,
58}
59
60impl<const R: usize, const C: usize, const A: usize, const D: usize> MemoryBank<R, C, A, D> {
61    pub fn new(timings: MemoryTimings) -> Self {
62        assert_eq!(R + C, A);
63        let t_ras = timings.t_ras() - 1;
64        let t_rc = timings.t_rc() - 1;
65        let t_rcd = timings.t_rcd() - 1;
66        let t_rp = timings.t_rp() - 1;
67        let t_refresh_max = timings.t_refresh_max() - 1;
68        let t_rfc = timings.t_rfc() - 1;
69        let t_wr = timings.t_wr() - 1;
70        Self {
71            clock: Default::default(),
72            cas_delay: Default::default(),
73            write_burst: Default::default(),
74            address: Default::default(),
75            burst_len: Default::default(),
76            cmd: Default::default(),
77            error: Default::default(),
78            busy: Default::default(),
79            write_data: Default::default(),
80            read_data: Default::default(),
81            read_valid: Default::default(),
82            select: Default::default(),
83            delay_line: Default::default(),
84            read_delay_line: Default::default(),
85            mem: Default::default(),
86            write_reg: Default::default(),
87            state: Default::default(),
88            auto_precharge: Default::default(),
89            active_row: Default::default(),
90            burst_counter: Default::default(),
91            active_col: Default::default(),
92            delay_counter: Default::default(),
93            refresh_counter: Default::default(),
94            refresh_active: Default::default(),
95            t_activate: Default::default(),
96            t_ras: Constant::new(t_ras.to_bits()),
97            t_rc: Constant::new(t_rc.to_bits()),
98            t_rcd: Constant::new(t_rcd.to_bits()),
99            t_rp: Constant::new(t_rp.to_bits()),
100            t_wr: Constant::new(t_wr.to_bits()),
101            t_refresh_max: Constant::new(t_refresh_max.to_bits()),
102            t_rfc: Constant::new(t_rfc.to_bits()),
103            row_shift: Constant::new(C.to_bits()),
104        }
105    }
106}
107
108impl<const R: usize, const C: usize, const A: usize, const D: usize> Logic
109    for MemoryBank<R, C, A, D>
110{
111    #[hdl_gen]
112    fn update(&mut self) {
113        // Clock the internal logic
114        self.mem.read_clock.next = self.clock.val();
115        self.mem.write_clock.next = self.clock.val();
116        dff_setup!(
117            self,
118            clock,
119            refresh_counter,
120            refresh_active,
121            write_reg,
122            state,
123            auto_precharge,
124            active_row,
125            burst_counter,
126            active_col,
127            delay_counter,
128            t_activate
129        );
130        clock!(self, clock, delay_line, read_delay_line);
131        self.delay_counter.d.next = self.delay_counter.q.val() + 1;
132        self.error.next = false;
133        // Model the row-column multiplexing
134        self.mem.read_address.next = (bit_cast::<A, R>(self.active_row.q.val())
135            << self.row_shift.val())
136            | bit_cast::<A, C>(self.active_col.q.val());
137        self.mem.write_address.next = (bit_cast::<A, R>(self.active_row.q.val())
138            << self.row_shift.val())
139            | bit_cast::<A, C>(self.active_col.q.val());
140        self.write_reg.d.next = self.write_data.val();
141        self.mem.write_data.next = self.write_reg.q.val();
142        self.mem.write_enable.next = false;
143        self.delay_line.data_in.next = self.mem.read_data.val();
144        self.read_data.next = self.delay_line.data_out.val();
145        self.delay_line.delay.next = self.cas_delay.val() - 2;
146        // Start counting cycles for how long the row is active
147        self.t_activate.d.next = self.t_activate.q.val() + 1;
148        self.busy.next = true;
149        self.read_delay_line.data_in.next = false;
150        self.read_delay_line.delay.next = self.cas_delay.val() - 1;
151        self.read_valid.next = self.read_delay_line.data_out.val();
152        self.refresh_counter.d.next = self.refresh_counter.q.val() + self.refresh_active.q.val();
153        match self.state.q.val() {
154            BankState::Boot => {
155                self.t_activate.d.next = 0xFFFF.into();
156                self.state.d.next = BankState::Idle;
157            }
158            BankState::Idle => {
159                self.busy.next = false;
160                if self.select.val() {
161                    match self.cmd.val() {
162                        SDRAMCommand::Active => {
163                            // Reset the activate timer
164                            if self.t_activate.q.val() < self.t_rc.val() {
165                                self.state.d.next = BankState::Error;
166                            } else {
167                                self.t_activate.d.next = 0.into();
168                                // Activate the given row.
169                                // Load the row into the row register
170                                self.active_row.d.next = self.address.val().get_bits::<R>(0);
171                                // Reset the delay timer
172                                self.delay_counter.d.next = 0.into();
173                                // Transition to the activating state.
174                                self.state.d.next = BankState::Active;
175                            }
176                        }
177                        SDRAMCommand::NOP => {}
178                        SDRAMCommand::Precharge => {} // See ISSI docs.  Precharging an idle bank is a NOP
179                        SDRAMCommand::AutoRefresh => {
180                            if self.refresh_active.q.val()
181                                & (self.refresh_counter.q.val() < self.t_rc.val())
182                            {
183                                self.state.d.next = BankState::Error;
184                            } else {
185                                self.state.d.next = BankState::Autorefreshing;
186                                self.refresh_active.d.next = true;
187                                self.refresh_counter.d.next = 0.into();
188                            }
189                        } // Handled at the chip level
190                        SDRAMCommand::LoadModeRegister => {} // Ignored by banks
191                        _ => {
192                            self.state.d.next = BankState::Error;
193                        }
194                    }
195                }
196            }
197            BankState::Active => {
198                if self.select.val() {
199                    match self.cmd.val() {
200                        SDRAMCommand::NOP => {}
201                        SDRAMCommand::Read => {
202                            if self.t_activate.q.val() < self.t_rcd.val() {
203                                self.state.d.next = BankState::Error;
204                            } else {
205                                // RCD is met, we want to read
206                                self.active_col.d.next = self.address.val().get_bits::<C>(0);
207                                self.burst_counter.d.next = 0.into();
208                                self.state.d.next = BankState::Reading;
209                                // Capture the auto precharge bit (bit 10) - this is the per the JEDEC spec
210                                self.auto_precharge.d.next = self.address.val().get_bit(10);
211                            }
212                        }
213                        SDRAMCommand::Write => {
214                            if self.t_activate.q.val() < self.t_rcd.val() {
215                                self.state.d.next = BankState::Error;
216                            } else {
217                                // RCD is met, we want to write
218                                self.active_col.d.next = self.address.val().get_bits::<C>(0);
219                                self.burst_counter.d.next = 0.into();
220                                self.state.d.next = BankState::Writing;
221                                // Capture the auto precharge bit (bit 10) - this is the per the JEDEC spec
222                                self.auto_precharge.d.next = self.address.val().get_bit(10);
223                            }
224                        }
225                        SDRAMCommand::Precharge => {
226                            if self.t_activate.q.val() < self.t_ras.val() {
227                                self.state.d.next = BankState::Error;
228                            } else {
229                                // RAS is met, we can close the current row
230                                self.delay_counter.d.next = 0.into();
231                                self.state.d.next = BankState::Precharging;
232                            }
233                        }
234                        _ => {
235                            self.state.d.next = BankState::Error;
236                        }
237                    }
238                }
239            }
240            BankState::Reading => {
241                // Process the read command
242                self.burst_counter.d.next = self.burst_counter.q.val() + 1;
243                self.active_col.d.next = self.active_col.q.val() + 1;
244                self.read_delay_line.data_in.next = true;
245                // Did the read finish?
246                if self.burst_counter.q.val() == self.burst_len.val() {
247                    self.read_delay_line.data_in.next = false;
248                    if self.auto_precharge.q.val() {
249                        self.delay_counter.d.next = 0.into();
250                        self.state.d.next = BankState::Precharging;
251                    } else {
252                        self.state.d.next = BankState::Active
253                    }
254                }
255                if self.select.val() {
256                    match self.cmd.val() {
257                        SDRAMCommand::NOP => {}
258                        SDRAMCommand::Read => {
259                            // RCD is met, we want to read
260                            self.active_col.d.next = self.address.val().get_bits::<C>(0);
261                            self.burst_counter.d.next = 0.into();
262                            // Capture the auto precharge bit (bit 10) - this is the per the JEDEC spec
263                            self.auto_precharge.d.next = self.address.val().get_bit(10);
264                            self.state.d.next = BankState::Reading;
265                        }
266                        SDRAMCommand::Precharge => {
267                            if self.auto_precharge.q.val() {
268                                self.state.d.next = BankState::Error;
269                            } else {
270                                self.delay_counter.d.next = 0.into();
271                                self.state.d.next = BankState::Precharging;
272                            }
273                        }
274                        _ => {
275                            self.state.d.next = BankState::Error;
276                        }
277                    }
278                }
279            }
280            BankState::Precharging => {
281                if self.delay_counter.q.val() == self.t_rp.val() {
282                    self.state.d.next = BankState::Idle;
283                }
284                if self.select.val() {
285                    match self.cmd.val() {
286                        SDRAMCommand::NOP => {}
287                        _ => {
288                            self.state.d.next = BankState::Error;
289                        }
290                    }
291                }
292            }
293            BankState::Autorefreshing => {
294                if self.refresh_counter.q.val() == self.t_rfc.val() {
295                    self.state.d.next = BankState::Idle;
296                }
297                if self.select.val() {
298                    match self.cmd.val() {
299                        SDRAMCommand::NOP => {}
300                        _ => {
301                            self.state.d.next = BankState::Error;
302                        }
303                    }
304                }
305            }
306            BankState::Writing => {
307                self.mem.write_enable.next = true;
308                // Process the write command
309                self.burst_counter.d.next = self.burst_counter.q.val() + 1;
310                self.active_col.d.next = self.active_col.q.val() + 1;
311                // Did the write finish?
312                if self.burst_counter.q.val() == self.burst_len.val() - 1 {
313                    self.delay_counter.d.next = 0.into();
314                    if self.auto_precharge.q.val() {
315                        self.state.d.next = BankState::Precharging;
316                    } else {
317                        self.state.d.next = BankState::WriteRecovery
318                    }
319                }
320                if self.select.val() {
321                    match self.cmd.val() {
322                        SDRAMCommand::NOP => {}
323                        SDRAMCommand::Write => {
324                            self.active_col.d.next = self.address.val().get_bits::<C>(0);
325                            self.burst_counter.d.next = 0.into();
326                            // Capture the auto precharge bit (bit 10) - this is the per the JEDEC spec
327                            self.auto_precharge.d.next = self.address.val().get_bit(10);
328                            self.state.d.next = BankState::Writing;
329                        }
330                        SDRAMCommand::Precharge => {
331                            if self.auto_precharge.q.val() {
332                                self.state.d.next = BankState::Error;
333                            } else {
334                                self.delay_counter.d.next = 0.into();
335                                self.state.d.next = BankState::Precharging;
336                            }
337                        }
338                        _ => {
339                            self.state.d.next = BankState::Error;
340                        }
341                    }
342                }
343            }
344            BankState::Error => {
345                self.error.next = true;
346            }
347            BankState::WriteRecovery => {
348                if self.delay_counter.q.val() == self.t_wr.val() {
349                    self.state.d.next = BankState::Active;
350                }
351                match self.cmd.val() {
352                    SDRAMCommand::NOP => {}
353                    SDRAMCommand::Read => {
354                        self.active_col.d.next = self.address.val().get_bits::<C>(0);
355                        self.burst_counter.d.next = 0.into();
356                        self.state.d.next = BankState::Reading;
357                        // Capture the auto precharge bit (bit 10) - this is the per the JEDEC spec
358                        self.auto_precharge.d.next = self.address.val().get_bit(10);
359                    }
360                    SDRAMCommand::Write => {
361                        self.active_col.d.next = self.address.val().get_bits::<C>(0);
362                        self.burst_counter.d.next = 0.into();
363                        self.state.d.next = BankState::Writing;
364                        // Capture the auto precharge bit (bit 10) - this is the per the JEDEC spec
365                        self.auto_precharge.d.next = self.address.val().get_bit(10);
366                    }
367                    _ => {
368                        self.state.d.next = BankState::Error;
369                    }
370                }
371            }
372            _ => {
373                self.state.d.next = BankState::Boot;
374            }
375        }
376        if self.refresh_counter.q.val() >= self.t_refresh_max.val() {
377            self.state.d.next = BankState::Error;
378        }
379    }
380}
381
382// For test purposes, we run the clock a lot faster...
383#[cfg(test)]
384fn mk_bank_sim() -> MemoryBank<5, 5, 10, 16> {
385    let mut uut = MemoryBank::new(MemoryTimings::mt48lc8m16a2(500e6));
386    uut.address.connect();
387    uut.cmd.connect();
388    uut.clock.connect();
389    uut.cas_delay.connect();
390    uut.write_burst.connect();
391    uut.burst_len.connect();
392    uut.write_data.connect();
393    uut.select.connect();
394    uut.connect_all();
395    uut.burst_len.next = 8.into();
396    uut.write_burst.next = true;
397    uut.cas_delay.next = 3.into();
398    uut.cmd.next = SDRAMCommand::NOP;
399    uut.select.next = true;
400    uut
401}
402
403#[test]
404fn test_bank_sim_synthesizes() {
405    let uut = mk_bank_sim();
406    let vlog = generate_verilog(&uut);
407    yosys_validate("sdram_bank", &vlog).unwrap();
408}
409
410#[test]
411fn test_bank_activation_immediate_close_is_ok_with_delay() {
412    let uut = mk_bank_sim();
413    let mut sim = Simulation::new();
414    // Clock period is 500 MHz or 2000ps
415    let clock_period = 2000;
416    sim.add_clock(clock_period / 2, |x: &mut Box<MemoryBank<5, 5, 10, 16>>| {
417        x.clock.next = !x.clock.val();
418    });
419    sim.add_testbench(move |mut sim: Sim<MemoryBank<5, 5, 10, 16>>| {
420        let mut x = sim.init()?;
421        let timing = MemoryTimings::mt48lc8m16a2(500e6);
422
423        wait_clock_true!(sim, clock, x);
424        wait_clock_cycles!(sim, clock, x, 30);
425        x.cmd.next = SDRAMCommand::Active;
426        x.address.next = 14.into();
427        wait_clock_cycle!(sim, clock, x);
428        let start_time = sim.time();
429        // Insert enough NOPS to meet the Active-to-precharge-time
430        // Allow for 1 clock delay while loading the precharge time
431        let wait_for_precharge =
432            timing.t_ras_row_active_min_time_nanoseconds * 1000.0 - clock_period as f64;
433        while sim.time() - start_time < wait_for_precharge as u64 {
434            x.cmd.next = SDRAMCommand::NOP;
435            wait_clock_cycle!(sim, clock, x);
436        }
437        x.cmd.next = SDRAMCommand::Precharge;
438        wait_clock_cycle!(sim, clock, x);
439        let start_time = sim.time();
440        let precharge_time = timing.t_rp_recharge_period_nanoseconds * 1000.0 - clock_period as f64;
441        while sim.time() - start_time < precharge_time as u64 {
442            x.cmd.next = SDRAMCommand::NOP;
443            wait_clock_cycle!(sim, clock, x);
444            sim_assert!(sim, x.state.q.val() != BankState::Idle, x);
445        }
446        wait_clock_cycle!(sim, clock, x);
447        sim_assert_eq!(sim, x.state.q.val(), BankState::Idle, x);
448        wait_clock_cycle!(sim, clock, x, 10);
449        sim_assert!(sim, !x.error.val(), x);
450        sim.done(x)
451    });
452    sim.run(Box::new(uut), 1_000_000).unwrap();
453}
454
455#[test]
456fn test_bank_activation_immediate_close_fails_for_timing() {
457    let uut = mk_bank_sim();
458    let mut sim = Simulation::new();
459    // Clock period is 500 MHz or 2000ps
460    let clock_period = 2000;
461    sim.add_clock(clock_period / 2, |x: &mut Box<MemoryBank<5, 5, 10, 16>>| {
462        x.clock.next = !x.clock.val();
463    });
464    sim.add_testbench(move |mut sim: Sim<MemoryBank<5, 5, 10, 16>>| {
465        let mut x = sim.init()?;
466        let timing = MemoryTimings::mt48lc8m16a2(500e6);
467
468        wait_clock_true!(sim, clock, x);
469        wait_clock_cycle!(sim, clock, x);
470        x.cmd.next = SDRAMCommand::Active;
471        x.address.next = 14.into();
472        wait_clock_cycle!(sim, clock, x);
473        let start_time = sim.time();
474        // Insert enough NOPS to meet the Active-to-precharge-time
475        // Allow for 1 clock delay while loading the precharge time
476        // Advance by 1 more clock so it fails
477        let wait_for_precharge =
478            (timing.t_ras_row_active_min_time_nanoseconds * 1000.0) as u64 - clock_period * 2;
479        while sim.time() - start_time < wait_for_precharge as u64 {
480            x.cmd.next = SDRAMCommand::NOP;
481            wait_clock_cycle!(sim, clock, x);
482        }
483        x.cmd.next = SDRAMCommand::Precharge;
484        wait_clock_cycle!(sim, clock, x);
485        x.cmd.next = SDRAMCommand::NOP;
486        wait_clock_cycle!(sim, clock, x, 10);
487        sim_assert!(sim, x.error.val(), x);
488        sim.done(x)
489    });
490    sim.run(Box::new(uut), 1_000_000).unwrap();
491}
492
493#[test]
494fn test_bank_write() {
495    let uut = mk_bank_sim();
496    let mut sim = Simulation::new();
497    // Clock period is 500 MHz or 2000ps
498    let clock_period = 2000;
499    sim.add_clock(clock_period / 2, |x: &mut Box<MemoryBank<5, 5, 10, 16>>| {
500        x.clock.next = !x.clock.val();
501    });
502    let data = [
503        0xABCD, 0xDEAD, 0xBEEF, 0x1234, 0xFACE, 0x5EA1, 0xCAFE, 0xBABE,
504    ];
505    sim.add_testbench(move |mut sim: Sim<MemoryBank<5, 5, 10, 16>>| {
506        let mut x = sim.init()?;
507        x = sim.watch(
508            |x| x.clock.val().clk & (x.cmd.val() == SDRAMCommand::Read),
509            x,
510        )?;
511        let cas_start_time = sim.time();
512        x = sim.watch(|x| x.clock.val().clk & x.read_valid.val(), x)?;
513        let cas_end_time = sim.time();
514        sim_assert!(
515            sim,
516            (cas_end_time - cas_start_time) == (x.cas_delay.val().index() as u64) * clock_period,
517            x
518        );
519        sim.done(x)
520    });
521    sim.add_testbench(move |mut sim: Sim<MemoryBank<5, 5, 10, 16>>| {
522        let mut x = sim.init()?;
523        for _ in 0..2 {
524            x = sim.watch(|x| !x.clock.val().clk & x.read_valid.val(), x)?;
525            for val in &data {
526                sim_assert!(sim, x.read_data.val() == *val, x);
527                wait_clock_cycle!(sim, clock, x);
528            }
529        }
530        sim.done(x)
531    });
532    sim.add_testbench(move |mut sim: Sim<MemoryBank<5, 5, 10, 16>>| {
533        let mut x = sim.init()?;
534        let timing = MemoryTimings::mt48lc8m16a2(500e6);
535
536        wait_clock_true!(sim, clock, x);
537        wait_clock_cycles!(sim, clock, x, 30);
538        x.cmd.next = SDRAMCommand::Active;
539        x.address.next = 14.into();
540        wait_clock_cycle!(sim, clock, x);
541        let start_time = sim.time();
542        // Insert enough NOPS to meet the Active-to-write-time
543        // Allow for 1 clock delay while loading the write command
544        let wait_for_active =
545            (timing.t_rcd_row_to_column_min_time_nanoseconds * 1000.0) as u64 - clock_period;
546        while sim.time() - start_time < wait_for_active as u64 {
547            x.cmd.next = SDRAMCommand::NOP;
548            wait_clock_cycle!(sim, clock, x);
549        }
550        x.cmd.next = SDRAMCommand::Write;
551        x.write_data.next = data[0].into();
552        x.address.next = 0.into();
553        wait_clock_cycle!(sim, clock, x);
554        for datum in data.iter().skip(1) {
555            x.cmd.next = SDRAMCommand::NOP;
556            x.write_data.next = (*datum).into();
557            wait_clock_cycle!(sim, clock, x);
558        }
559        x.cmd.next = SDRAMCommand::NOP;
560        wait_clock_cycles!(sim, clock, x, 10);
561        // Read the data back out
562        x.cmd.next = SDRAMCommand::Read;
563        x.address.next = 0.into();
564        wait_clock_cycle!(sim, clock, x);
565        x.cmd.next = SDRAMCommand::NOP;
566        wait_clock_cycles!(sim, clock, x, 8);
567        // Read the data back out - with auto precharge
568        x.cmd.next = SDRAMCommand::Read;
569        x.address.next = 1024.into();
570        wait_clock_cycle!(sim, clock, x);
571        x.cmd.next = SDRAMCommand::NOP;
572        wait_clock_cycles!(sim, clock, x, 10);
573        let precharge_clocks = timing.t_rp();
574        wait_clock_cycles!(sim, clock, x, precharge_clocks);
575        sim_assert!(sim, !x.busy.val(), x);
576        sim_assert!(sim, !x.error.val(), x);
577        sim.done(x)
578    });
579    sim.run_to_file(Box::new(uut), 1_000_000, &vcd_path!("sdram_write.vcd"))
580        .unwrap();
581}