use crate::{dff::DFF, dff_setup, fifo::async_fifo::AsynchronousFIFO, sdram::SDRAMDriver};
use rust_hdl_core::prelude::*;
use super::{burst_controller::SDRAMBurstController, timings::MemoryTimings, OutputBuffer};
#[derive(Copy, Clone, Debug, PartialEq, LogicState)]
enum State {
Idle,
Read,
Write,
Busy,
}
#[derive(LogicBlock)]
pub struct SDRAMFIFOController<
const R: usize, const C: usize, const L: u32, const D: usize, const A: usize, > {
pub clock: Signal<In, Clock>,
pub sdram: SDRAMDriver<D>,
pub ram_clock: Signal<In, Clock>,
pub data_in: Signal<In, Bits<D>>,
pub write: Signal<In, Bit>,
pub full: Signal<Out, Bit>,
pub data_out: Signal<Out, Bits<D>>,
pub read: Signal<In, Bit>,
pub empty: Signal<Out, Bit>,
pub overflow: Signal<Out, Bit>,
pub underflow: Signal<Out, Bit>,
pub status: Signal<Out, Bits<8>>,
controller: SDRAMBurstController<R, C, L, D>,
fp: AsynchronousFIFO<Bits<D>, 5, 6, L>,
bp: AsynchronousFIFO<Bits<D>, 5, 6, L>,
can_write: DFF<Bit>,
can_read: DFF<Bit>,
read_pointer: DFF<Bits<A>>,
write_pointer: DFF<Bits<A>>,
dram_is_empty: DFF<Bit>,
dram_is_full: DFF<Bit>,
state: DFF<State>,
line_to_word_ratio: Constant<Bits<A>>,
fill: DFF<Bits<A>>,
fill_0: Constant<Bits<A>>,
fill_1: Constant<Bits<A>>,
fill_2: Constant<Bits<A>>,
fill_3: Constant<Bits<A>>,
}
impl<const R: usize, const C: usize, const L: u32, const D: usize, const A: usize>
SDRAMFIFOController<R, C, L, D, A>
{
pub fn new(cas_delay: u32, timings: MemoryTimings, buffer: OutputBuffer) -> Self {
assert_eq!((1 << C) % L, 0);
assert_eq!(A, C + R + 2);
assert!(L < 32);
let banks = 4; let bit_count = R * C * D * banks;
let entry_bits = L as usize * D;
let max_entries = bit_count / entry_bits;
let entries_per_led = max_entries / 5;
Self {
clock: Default::default(),
sdram: Default::default(),
ram_clock: Default::default(),
data_in: Default::default(),
write: Default::default(),
full: Default::default(),
data_out: Default::default(),
read: Default::default(),
empty: Default::default(),
overflow: Default::default(),
underflow: Default::default(),
status: Default::default(),
controller: SDRAMBurstController::new(cas_delay, timings, buffer),
fp: Default::default(),
bp: Default::default(),
can_write: Default::default(),
can_read: Default::default(),
read_pointer: Default::default(),
write_pointer: Default::default(),
dram_is_empty: Default::default(),
dram_is_full: Default::default(),
state: Default::default(),
line_to_word_ratio: Constant::new(L.to_bits()),
fill: Default::default(),
fill_0: Constant::new((entries_per_led).to_bits()),
fill_1: Constant::new((entries_per_led * 2).to_bits()),
fill_2: Constant::new((entries_per_led * 3).to_bits()),
fill_3: Constant::new((entries_per_led * 4).to_bits()),
}
}
}
impl<const R: usize, const C: usize, const L: u32, const D: usize, const A: usize> Logic
for SDRAMFIFOController<R, C, L, D, A>
{
#[hdl_gen]
fn update(&mut self) {
clock!(self, ram_clock, controller);
SDRAMDriver::<D>::link(&mut self.sdram, &mut self.controller.sdram);
dff_setup!(
self,
ram_clock,
read_pointer,
write_pointer,
dram_is_empty,
dram_is_full,
can_read,
can_write,
state,
fill
);
self.fp.write_clock.next = self.clock.val();
self.fp.read_clock.next = self.ram_clock.val();
self.bp.write_clock.next = self.ram_clock.val();
self.bp.read_clock.next = self.clock.val();
self.fp.data_in.next = self.data_in.val();
self.fp.write.next = self.write.val();
self.full.next = self.fp.full.val();
self.overflow.next = self.fp.overflow.val();
self.data_out.next = self.bp.data_out.val();
self.bp.read.next = self.read.val();
self.empty.next = self.bp.empty.val();
self.underflow.next = self.bp.underflow.val();
self.controller.data_in.next = self.fp.data_out.val();
self.fp.read.next = self.controller.data_strobe.val();
self.bp.data_in.next = self.controller.data_out.val();
self.bp.write.next = self.controller.data_valid.val();
self.dram_is_empty.d.next = self.read_pointer.q.val() == self.write_pointer.q.val();
self.dram_is_full.d.next = (self.write_pointer.q.val() + self.line_to_word_ratio.val())
== self.read_pointer.q.val();
self.can_write.d.next = !self.dram_is_full.q.val() & !self.fp.almost_empty.val();
self.can_read.d.next = !self.dram_is_empty.q.val() & !self.bp.almost_full.val();
self.controller.cmd_address.next = 0.into();
self.controller.write_not_read.next = false;
self.controller.cmd_strobe.next = false;
self.fill.clock.next = self.ram_clock.val();
self.fill.d.next = self.fill.q.val();
match self.state.q.val() {
State::Idle => {
if !self.controller.busy.val() {
if self.can_read.q.val() {
self.state.d.next = State::Read;
self.controller.cmd_address.next =
bit_cast::<32, A>(self.read_pointer.q.val());
self.controller.write_not_read.next = false;
self.controller.cmd_strobe.next = true;
} else if self.can_write.q.val() {
self.state.d.next = State::Write;
self.controller.cmd_address.next =
bit_cast::<32, A>(self.write_pointer.q.val());
self.controller.write_not_read.next = true;
self.controller.cmd_strobe.next = true;
}
}
}
State::Read => {
self.read_pointer.d.next =
self.read_pointer.q.val() + self.line_to_word_ratio.val();
self.fill.d.next = self.fill.q.val() - 1;
self.state.d.next = State::Busy;
}
State::Write => {
self.write_pointer.d.next =
self.write_pointer.q.val() + self.line_to_word_ratio.val();
self.fill.d.next = self.fill.q.val() + 1;
self.state.d.next = State::Busy;
}
State::Busy => {
if !self.controller.busy.val() {
self.state.d.next = State::Idle;
}
}
_ => {
self.state.d.next = State::Idle;
}
}
self.status.next = 0.into();
if self.fill.q.val() > self.fill_0.val() {
self.status.next = self.status.val() | 1;
}
if self.fill.q.val() > self.fill_1.val() {
self.status.next = self.status.val() | 2;
}
if self.fill.q.val() > self.fill_2.val() {
self.status.next = self.status.val() | 4;
}
if self.fill.q.val() > self.fill_3.val() {
self.status.next = self.status.val() | 8;
}
if self.underflow.val() | self.overflow.val() {
self.status.next = self.status.val() | 16;
}
if self.dram_is_empty.q.val() {
self.status.next = self.status.val() | 32;
}
if self.fp.empty.val() {
self.status.next = self.status.val() | 64;
}
if self.bp.full.val() {
self.status.next = self.status.val() | 128;
}
}
}