Compare commits
5 Commits
8a9dde6119
...
0adb0d5c51
Author | SHA1 | Date |
---|---|---|
Astro | 0adb0d5c51 | |
Astro | dd0fe054d7 | |
Astro | 1dbb358a4c | |
Astro | b94afa1581 | |
Astro | 0d1cf04a34 |
|
@ -1,5 +1,6 @@
|
||||||
//! Quad-SPI Flash Controller
|
//! Quad-SPI Flash Controller
|
||||||
|
|
||||||
|
use crate::{print, println};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use crate::regs::{RegisterR, RegisterW, RegisterRW};
|
use crate::regs::{RegisterR, RegisterW, RegisterRW};
|
||||||
use super::slcr;
|
use super::slcr;
|
||||||
|
@ -10,6 +11,8 @@ mod bytes;
|
||||||
pub use bytes::{BytesTransferExt, BytesTransfer};
|
pub use bytes::{BytesTransferExt, BytesTransfer};
|
||||||
mod spi_flash_register;
|
mod spi_flash_register;
|
||||||
use spi_flash_register::*;
|
use spi_flash_register::*;
|
||||||
|
mod transfer;
|
||||||
|
use transfer::Transfer;
|
||||||
|
|
||||||
const FLASH_BAUD_RATE: u32 = 50_000_000;
|
const FLASH_BAUD_RATE: u32 = 50_000_000;
|
||||||
/// 16 MB
|
/// 16 MB
|
||||||
|
@ -24,6 +27,12 @@ const INST_READ: u8 = 0x03;
|
||||||
const INST_WRDI: u8 = 0x04;
|
const INST_WRDI: u8 = 0x04;
|
||||||
/// Instruction: Write Enable
|
/// Instruction: Write Enable
|
||||||
const INST_WREN: u8 = 0x06;
|
const INST_WREN: u8 = 0x06;
|
||||||
|
/// Instruction: Program page
|
||||||
|
const INST_PP: u8 = 02;
|
||||||
|
/// Instruction: Sector Erase
|
||||||
|
const INST_SE: u8 = 0xD8;
|
||||||
|
/// Instruction: Erase 4K Block
|
||||||
|
const INST_BE_4K: u8 = 0x20;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum SpiWord {
|
pub enum SpiWord {
|
||||||
|
@ -357,9 +366,40 @@ impl Flash<Manual> {
|
||||||
|
|
||||||
pub fn read_reg<R: SpiFlashRegister>(&mut self) -> R {
|
pub fn read_reg<R: SpiFlashRegister>(&mut self) -> R {
|
||||||
let args = Some(R::inst_code());
|
let args = Some(R::inst_code());
|
||||||
let transfer = self.transfer(args.into_iter(), R::transfer_len())
|
let transfer = self.transfer(args.into_iter(), 2)
|
||||||
.bytes_transfer().skip(1);
|
.bytes_transfer();
|
||||||
R::new(transfer)
|
R::new(transfer.skip(1).next().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_reg_until<R, F, A>(&mut self, f: F) -> A
|
||||||
|
where
|
||||||
|
R: SpiFlashRegister,
|
||||||
|
F: Fn(R) -> Option<A>,
|
||||||
|
{
|
||||||
|
let mut result = None;
|
||||||
|
while result.is_none() {
|
||||||
|
let args = Some(R::inst_code());
|
||||||
|
for b in self.transfer(args.into_iter(), 32)
|
||||||
|
.bytes_transfer().skip(1) {
|
||||||
|
result = f(R::new(b));
|
||||||
|
|
||||||
|
if result.is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Status Register-1 remains `0x00` immediately after invoking a command.
|
||||||
|
fn wait_while_sr1_zeroed(&mut self) -> SR1 {
|
||||||
|
self.read_reg_until::<SR1, _, SR1>(|sr1|
|
||||||
|
if sr1.is_zeroed() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(sr1)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read Identification
|
/// Read Identification
|
||||||
|
@ -378,12 +418,63 @@ impl Flash<Manual> {
|
||||||
.bytes_transfer().skip(6).take(len)
|
.bytes_transfer().skip(6).take(len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn erase(&mut self, offset: u32) {
|
||||||
|
let args = Some(((INST_BE_4K as u32) << 24) | (offset as u32));
|
||||||
|
self.transfer(args.into_iter(), 4);
|
||||||
|
|
||||||
|
let sr1 = self.wait_while_sr1_zeroed();
|
||||||
|
|
||||||
|
if sr1.e_err() {
|
||||||
|
println!("E_ERR");
|
||||||
|
} else if sr1.p_err() {
|
||||||
|
println!("P_ERR");
|
||||||
|
} else if sr1.wip() {
|
||||||
|
print!("Erase in progress");
|
||||||
|
while self.read_reg::<SR1>().wip() {
|
||||||
|
print!(".");
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
} else {
|
||||||
|
println!("erased? sr1={:02X}", sr1.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program<I: Iterator<Item=u32>>(&mut self, offset: u32, data: I) {
|
||||||
|
{
|
||||||
|
let len = 4 + 4 * data.size_hint().0;
|
||||||
|
let args = Some(SpiWord::W32(((INST_PP as u32) << 24) | (offset as u32)))
|
||||||
|
.into_iter()
|
||||||
|
.chain(data.map(SpiWord::W32));
|
||||||
|
self.transfer(args, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// let sr1 = self.wait_while_sr1_zeroed();
|
||||||
|
let sr1 = self.read_reg::<SR1>();
|
||||||
|
|
||||||
|
if sr1.e_err() {
|
||||||
|
println!("E_ERR");
|
||||||
|
} else if sr1.p_err() {
|
||||||
|
println!("P_ERR");
|
||||||
|
} else if sr1.wip() {
|
||||||
|
println!("Program in progress");
|
||||||
|
while self.read_reg::<SR1>().wip() {
|
||||||
|
print!(".");
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
} else {
|
||||||
|
println!("programmed? sr1={:02X}", sr1.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_enabled<F: Fn(&mut Self) -> R, R>(&mut self, f: F) -> R {
|
pub fn write_enabled<F: Fn(&mut Self) -> R, R>(&mut self, f: F) -> R {
|
||||||
// Write Enable
|
// Write Enable
|
||||||
let args = Some(INST_WREN);
|
let args = Some(INST_WREN);
|
||||||
self.transfer(args.into_iter(), 1);
|
self.transfer(args.into_iter(), 1);
|
||||||
self.regs.gpio.modify(|_, w| w.wp_n(true));
|
self.regs.gpio.modify(|_, w| w.wp_n(true));
|
||||||
while !self.read_reg::<SR1>().wel() {}
|
let sr1 = self.wait_while_sr1_zeroed();
|
||||||
|
if !sr1.wel() {
|
||||||
|
panic!("Cannot write-enable flash");
|
||||||
|
}
|
||||||
|
|
||||||
let result = f(self);
|
let result = f(self);
|
||||||
|
|
||||||
|
@ -391,7 +482,6 @@ impl Flash<Manual> {
|
||||||
let args = Some(INST_WRDI);
|
let args = Some(INST_WRDI);
|
||||||
self.transfer(args.into_iter(), 1);
|
self.transfer(args.into_iter(), 1);
|
||||||
self.regs.gpio.modify(|_, w| w.wp_n(false));
|
self.regs.gpio.modify(|_, w| w.wp_n(false));
|
||||||
while self.read_reg::<SR1>().wel() {}
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -403,128 +493,4 @@ impl Flash<Manual> {
|
||||||
{
|
{
|
||||||
Transfer::new(self, args, len)
|
Transfer::new(self, args, len)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Transfer<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> {
|
|
||||||
flash: &'a mut Flash<Manual>,
|
|
||||||
args: Args,
|
|
||||||
sent: usize,
|
|
||||||
received: usize,
|
|
||||||
len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Transfer<'a, Args, W> {
|
|
||||||
pub fn new(flash: &'a mut Flash<Manual>, args: Args, len: usize) -> Self {
|
|
||||||
flash.regs.config.modify(|_, w| w.pcs(false));
|
|
||||||
flash.regs.enable.write(
|
|
||||||
regs::Enable::zeroed()
|
|
||||||
.spi_en(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut xfer = Transfer {
|
|
||||||
flash,
|
|
||||||
args,
|
|
||||||
sent: 0,
|
|
||||||
received: 0,
|
|
||||||
len,
|
|
||||||
};
|
|
||||||
xfer.fill_tx_fifo();
|
|
||||||
xfer.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
|
||||||
xfer
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_tx_fifo(&mut self) {
|
|
||||||
while self.sent < self.len && !self.flash.regs.intr_status.read().tx_fifo_full() {
|
|
||||||
let arg = self.args.next()
|
|
||||||
.map(|n| n.into())
|
|
||||||
.unwrap_or(SpiWord::W32(0));
|
|
||||||
match arg {
|
|
||||||
SpiWord::W32(w) => {
|
|
||||||
// println!("txd0 {:08X}", w);
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd0.write(w);
|
|
||||||
}
|
|
||||||
self.sent += 4;
|
|
||||||
}
|
|
||||||
// Only txd0 can be used without flushing
|
|
||||||
_ => {
|
|
||||||
if !self.flash.regs.intr_status.read().tx_fifo_not_full() {
|
|
||||||
// Flush if neccessary
|
|
||||||
self.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
|
||||||
self.flash.wait_tx_fifo_flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
match arg {
|
|
||||||
SpiWord::W8(w) => {
|
|
||||||
// println!("txd1 {:02X}", w);
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd1.write(u32::from(w) << 24);
|
|
||||||
}
|
|
||||||
self.sent += 1;
|
|
||||||
}
|
|
||||||
SpiWord::W16(w) => {
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd2.write(u32::from(w) << 16);
|
|
||||||
}
|
|
||||||
self.sent += 2;
|
|
||||||
}
|
|
||||||
SpiWord::W24(w) => {
|
|
||||||
unsafe {
|
|
||||||
self.flash.regs.txd3.write(w << 8);
|
|
||||||
}
|
|
||||||
self.sent += 3;
|
|
||||||
}
|
|
||||||
SpiWord::W32(_) => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
|
||||||
self.flash.wait_tx_fifo_flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_read(&mut self) -> bool {
|
|
||||||
self.flash.regs.intr_status.read().rx_fifo_not_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&mut self) -> u32 {
|
|
||||||
let rx = self.flash.regs.rx_data.read();
|
|
||||||
self.received += 4;
|
|
||||||
rx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Drop for Transfer<'a, Args, W> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Discard remaining rx_data
|
|
||||||
while self.can_read() {
|
|
||||||
self.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop
|
|
||||||
self.flash.regs.enable.write(
|
|
||||||
regs::Enable::zeroed()
|
|
||||||
.spi_en(false)
|
|
||||||
);
|
|
||||||
self.flash.regs.config.modify(|_, w| w
|
|
||||||
.pcs(true)
|
|
||||||
.man_start_com(false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Iterator for Transfer<'a, Args, W> {
|
|
||||||
type Item = u32;
|
|
||||||
|
|
||||||
fn next<'s>(&'s mut self) -> Option<u32> {
|
|
||||||
if self.received >= self.len {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fill_tx_fifo();
|
|
||||||
|
|
||||||
while !self.can_read() {}
|
|
||||||
Some(self.read())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ use bit_field::BitField;
|
||||||
|
|
||||||
pub trait SpiFlashRegister {
|
pub trait SpiFlashRegister {
|
||||||
fn inst_code() -> u8;
|
fn inst_code() -> u8;
|
||||||
fn transfer_len() -> usize;
|
fn new(src: u8) -> Self;
|
||||||
fn new<I: Iterator<Item=u8>>(src: I) -> Self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! u8_register {
|
macro_rules! u8_register {
|
||||||
($name: ident, $inst_code: expr) => {
|
($name: ident, $doc: tt, $inst_code: expr) => {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[doc=$doc]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
pub inner: u8,
|
pub inner: u8,
|
||||||
}
|
}
|
||||||
|
@ -18,21 +18,23 @@ macro_rules! u8_register {
|
||||||
$inst_code
|
$inst_code
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transfer_len() -> usize {
|
fn new(src: u8) -> Self {
|
||||||
2
|
$name {
|
||||||
|
inner: src,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new<I: Iterator<Item=u8>>(mut src: I) -> Self {
|
impl $name {
|
||||||
$name {
|
pub fn is_zeroed(&self) -> bool {
|
||||||
inner: src.next().unwrap(),
|
self.inner == 0
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
u8_register!(CR, 0x35);
|
u8_register!(CR, "Configuration Register", 0x35);
|
||||||
u8_register!(SR1, 0x05);
|
u8_register!(SR1, "Status Register-1", 0x05);
|
||||||
impl SR1 {
|
impl SR1 {
|
||||||
/// Write In Progress
|
/// Write In Progress
|
||||||
pub fn wip(&self) -> bool {
|
pub fn wip(&self) -> bool {
|
||||||
|
@ -55,4 +57,5 @@ impl SR1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u8_register!(SR2, 0x07);
|
u8_register!(SR2, "Status Register-2", 0x07);
|
||||||
|
u8_register!(BA, "Bank Address Register", 0xB9);
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
use crate::regs::{RegisterR, RegisterW, RegisterRW};
|
||||||
|
use super::regs;
|
||||||
|
use super::{SpiWord, Flash, Manual};
|
||||||
|
|
||||||
|
pub struct Transfer<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> {
|
||||||
|
flash: &'a mut Flash<Manual>,
|
||||||
|
args: Args,
|
||||||
|
sent: usize,
|
||||||
|
received: usize,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Transfer<'a, Args, W> {
|
||||||
|
pub fn new(flash: &'a mut Flash<Manual>, args: Args, len: usize) -> Self {
|
||||||
|
flash.regs.config.modify(|_, w| w.pcs(false));
|
||||||
|
flash.regs.enable.write(
|
||||||
|
regs::Enable::zeroed()
|
||||||
|
.spi_en(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut xfer = Transfer {
|
||||||
|
flash,
|
||||||
|
args,
|
||||||
|
sent: 0,
|
||||||
|
received: 0,
|
||||||
|
len,
|
||||||
|
};
|
||||||
|
xfer.fill_tx_fifo();
|
||||||
|
xfer.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
||||||
|
xfer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_tx_fifo(&mut self) {
|
||||||
|
while self.sent < self.len && !self.flash.regs.intr_status.read().tx_fifo_full() {
|
||||||
|
let arg = self.args.next()
|
||||||
|
.map(|n| n.into())
|
||||||
|
.unwrap_or(SpiWord::W32(0));
|
||||||
|
match arg {
|
||||||
|
SpiWord::W32(w) => {
|
||||||
|
// println!("txd0 {:08X}", w);
|
||||||
|
unsafe {
|
||||||
|
self.flash.regs.txd0.write(w);
|
||||||
|
}
|
||||||
|
self.sent += 4;
|
||||||
|
}
|
||||||
|
// Only txd0 can be used without flushing
|
||||||
|
_ => {
|
||||||
|
if !self.flash.regs.intr_status.read().tx_fifo_not_full() {
|
||||||
|
// Flush if necessary
|
||||||
|
self.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
||||||
|
self.flash.wait_tx_fifo_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
SpiWord::W8(w) => {
|
||||||
|
// println!("txd1 {:02X}", w);
|
||||||
|
unsafe {
|
||||||
|
self.flash.regs.txd1.write(u32::from(w) << 24);
|
||||||
|
}
|
||||||
|
self.sent += 1;
|
||||||
|
}
|
||||||
|
SpiWord::W16(w) => {
|
||||||
|
unsafe {
|
||||||
|
self.flash.regs.txd2.write(u32::from(w) << 16);
|
||||||
|
}
|
||||||
|
self.sent += 2;
|
||||||
|
}
|
||||||
|
SpiWord::W24(w) => {
|
||||||
|
unsafe {
|
||||||
|
self.flash.regs.txd3.write(w << 8);
|
||||||
|
}
|
||||||
|
self.sent += 3;
|
||||||
|
}
|
||||||
|
SpiWord::W32(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flash.regs.config.modify(|_, w| w.man_start_com(true));
|
||||||
|
self.flash.wait_tx_fifo_flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_read(&mut self) -> bool {
|
||||||
|
self.flash.regs.intr_status.read().rx_fifo_not_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self) -> u32 {
|
||||||
|
let rx = self.flash.regs.rx_data.read();
|
||||||
|
self.received += 4;
|
||||||
|
rx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Drop for Transfer<'a, Args, W> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Discard remaining rx_data
|
||||||
|
while self.can_read() {
|
||||||
|
self.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop
|
||||||
|
self.flash.regs.enable.write(
|
||||||
|
regs::Enable::zeroed()
|
||||||
|
.spi_en(false)
|
||||||
|
);
|
||||||
|
self.flash.regs.config.modify(|_, w| w
|
||||||
|
.pcs(true)
|
||||||
|
.man_start_com(false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Iterator for Transfer<'a, Args, W> {
|
||||||
|
type Item = u32;
|
||||||
|
|
||||||
|
fn next<'s>(&'s mut self) -> Option<u32> {
|
||||||
|
if self.received >= self.len {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fill_tx_fifo();
|
||||||
|
|
||||||
|
while !self.can_read() {}
|
||||||
|
Some(self.read())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue