use core::cmp;
use core::convert::TryInto;
use core::marker::PhantomData;
use crate::hal::blocking::delay::DelayUs;
use crate::stm32;
use stm32h7xx_hal::rcc::{rec, CoreClocks};
use crate::fmc::{Fmc, FmcBank, PinsSdram};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FmcSdramConfiguration {
pub column_bits: u8,
pub row_bits: u8,
pub memory_data_width: u8,
pub internal_banks: u8,
pub cas_latency: u8,
pub write_protection: bool,
pub sd_clock_divide: u8,
pub read_burst: bool,
pub read_pipe_delay_cycles: u8,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FmcSdramTiming {
pub startup_delay_ns: u32,
pub max_sd_clock_hz: u32,
pub refresh_period_ns: u32,
pub mode_register_to_active: u8,
pub exit_self_refresh: u8,
pub active_to_precharge: u8,
pub row_cycle: u8,
pub row_precharge: u8,
pub row_to_column: u8,
}
pub trait SdramChip {
const MODE_REGISTER: u16;
const CONFIG: FmcSdramConfiguration;
const TIMING: FmcSdramTiming;
}
#[allow(missing_debug_implementations)]
pub struct Sdram<IC, PINS> {
mem: Fmc,
_pins: PhantomData<PINS>,
_chip: PhantomData<IC>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(unused)]
enum SdramCommand {
NormalMode,
ClkEnable,
Pall,
Autorefresh(u8),
LoadMode(u16),
Selfrefresh,
Powerdown,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(unused)]
enum SdramTargetBank {
Bank1,
Bank2,
Both,
}
impl<IC, PINS> Sdram<IC, PINS>
where
IC: SdramChip,
PINS: PinsSdram<stm32::FMC>,
{
pub fn new(
fmc: stm32::FMC,
rec_fmc: rec::Fmc,
_pins: PINS,
_chip: IC,
) -> Self {
assert!(
PINS::ADDRESS_LINES >= IC::CONFIG.row_bits,
"Not enough address pins to access all SDRAM rows"
);
assert!(
PINS::ADDRESS_LINES >= IC::CONFIG.column_bits,
"Not enough address pins to access all SDRAM colums"
);
assert!(
PINS::NUMBER_INTERNAL_BANKS >= IC::CONFIG.internal_banks,
"Not enough bank address pins to access all internal banks"
);
Sdram {
mem: Fmc::new(fmc, rec_fmc),
_pins: PhantomData,
_chip: PhantomData,
}
}
pub unsafe fn new_unchecked(
fmc: stm32::FMC,
rec_fmc: rec::Fmc,
_pins: PINS,
_chip: IC,
) -> Self {
Sdram {
mem: Fmc::new(fmc, rec_fmc),
_pins: PhantomData,
_chip: PhantomData,
}
}
pub fn init<D>(
&mut self,
delay: &mut D,
core_clocks: CoreClocks,
) -> *mut u32
where
D: DelayUs<u8>,
{
use SdramCommand::*;
use SdramTargetBank::*;
let bank = match PINS::EXTERNAL_BANK {
1 => Bank1,
2 => Bank2,
_ => unimplemented!(),
};
assert!(
IC::CONFIG.sd_clock_divide >= 2 && IC::CONFIG.sd_clock_divide <= 3,
"SD clock divider is invalid!"
);
let sd_clock_hz = {
let fmc_ker_ck_hz = self
.mem
.get_ker_clk(core_clocks)
.expect("FMC kernel clock is not running!")
.0;
fmc_ker_ck_hz / IC::CONFIG.sd_clock_divide as u32
};
assert!(
sd_clock_hz <= IC::TIMING.max_sd_clock_hz,
"FMC kernel clock is too fast for the SD
clock period of the SDRAM!"
);
fmc_trace!(
"FMC clock {:?} (Max {:?})",
sd_clock_hz,
IC::TIMING.max_sd_clock_hz
);
unsafe {
self.set_features_timings(
PINS::EXTERNAL_BANK,
IC::CONFIG,
IC::TIMING,
);
self.mem.enable();
self.send_command(ClkEnable, bank);
let startup_delay_us = (IC::TIMING.startup_delay_ns + 999) / 1000;
delay.delay_us(startup_delay_us.try_into().unwrap());
self.send_command(Pall, bank);
self.send_command(Autorefresh(8), bank);
self.send_command(LoadMode(IC::MODE_REGISTER), bank);
let refresh_counter_top = ((IC::TIMING.refresh_period_ns as u64
* sd_clock_hz as u64)
/ 1_000_000_000)
- 20;
assert!(
refresh_counter_top >= 41 && refresh_counter_top < (1 << 13),
"Impossible configuration for H7 FMC Controller"
);
self.mem
.fmc
.sdrtr
.modify(|_, w| w.count().bits(refresh_counter_top as u16));
}
match PINS::EXTERNAL_BANK {
1 => FmcBank::Bank5.ptr(),
2 => FmcBank::Bank6.ptr(),
_ => unimplemented!(),
}
}
unsafe fn set_features_timings(
&mut self,
sdram_bank: u8,
config: FmcSdramConfiguration,
timing: FmcSdramTiming,
) {
let sd = match sdram_bank {
1 => self.mem.fmc.sdbank1(),
2 => self.mem.fmc.sdbank2(),
_ => panic!(),
};
assert!(
config.cas_latency >= 1 && config.cas_latency <= 3,
"Impossible configuration for H7 FMC Controller"
);
assert!(
config.row_bits >= 11 && config.row_bits <= 13,
"Impossible configuration for H7 FMC Controller"
);
assert!(
config.column_bits >= 8 && config.column_bits <= 11,
"Impossible configuration for H7 FMC Controller"
);
assert!(
config.read_pipe_delay_cycles <= 2,
"Impossible configuration for H7 FMC Controller"
);
self.mem.fmc.sdbank1().sdcr.modify(|_, w| {
w.rpipe()
.bits(config.read_pipe_delay_cycles)
.rburst()
.bit(config.read_burst)
.sdclk()
.bits(config.sd_clock_divide)
});
sd.sdcr.modify(|_, w| {
w.wp()
.bit(config.write_protection)
.cas()
.bits(config.cas_latency)
.nb()
.bit(match config.internal_banks {
2 => false,
4 => true,
_ => {
panic!("Impossible configuration for H7 FMC Controller")
}
})
.mwid()
.bits(match config.memory_data_width {
8 => 0,
16 => 1,
32 => 2,
_ => {
panic!("Impossible configuration for H7 FMC Controller")
}
})
.nr()
.bits(config.row_bits - 11)
.nc()
.bits(config.column_bits - 8)
});
let minimum_self_refresh = timing.active_to_precharge;
let write_recovery_self_refresh =
minimum_self_refresh - timing.row_to_column;
let write_recovery_row_cycle =
timing.row_cycle - timing.row_to_column - timing.row_precharge;
let write_recovery =
cmp::max(write_recovery_self_refresh, write_recovery_row_cycle);
self.mem.fmc.sdbank1().sdtr.modify(|_, w| {
w.trc()
.bits(timing.row_cycle - 1)
.trp()
.bits(timing.row_precharge - 1)
});
sd.sdtr.modify(|_, w| {
w.trcd()
.bits(timing.row_to_column - 1)
.twr()
.bits(write_recovery - 1)
.tras()
.bits(minimum_self_refresh - 1)
.txsr()
.bits(timing.exit_self_refresh - 1)
.tmrd()
.bits(timing.mode_register_to_active - 1)
});
}
unsafe fn send_command(
&mut self,
mode: SdramCommand,
target: SdramTargetBank,
) {
use SdramCommand::*;
use SdramTargetBank::*;
let (cmd, number_refresh, mode_reg) = match mode {
NormalMode => (0x00, 1, 0),
ClkEnable => (0x01, 1, 0),
Pall => (0x02, 1, 0),
Autorefresh(a) => (0x03, a, 0),
LoadMode(mr) => (0x04, 1, mr),
Selfrefresh => (0x05, 1, 0),
Powerdown => (0x06, 1, 0),
};
let (b1, b2) = match target {
Bank1 => (true, false),
Bank2 => (false, true),
Both => (true, true),
};
self.mem.fmc.sdcmr.modify(|_, w| {
w.mrd()
.bits(mode_reg)
.nrfs()
.bits(number_refresh)
.ctb1()
.bit(b1)
.ctb2()
.bit(b2)
.mode()
.bits(cmd)
});
}
}