diff options
Diffstat (limited to 'drivers/flexspi')
| -rw-r--r-- | drivers/flexspi/Cargo.toml | 7 | ||||
| -rw-r--r-- | drivers/flexspi/src/lib.rs | 882 |
2 files changed, 889 insertions, 0 deletions
diff --git a/drivers/flexspi/Cargo.toml b/drivers/flexspi/Cargo.toml new file mode 100644 index 0000000..1b33074 --- /dev/null +++ b/drivers/flexspi/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "imxrt-drivers-flexspi" +version = "0.1.0" +edition = "2024" + +[dependencies] +ral-registers = { workspace = true } diff --git a/drivers/flexspi/src/lib.rs b/drivers/flexspi/src/lib.rs new file mode 100644 index 0000000..b7d3a11 --- /dev/null +++ b/drivers/flexspi/src/lib.rs @@ -0,0 +1,882 @@ +#![no_std] + +pub type Instance = ral_registers::Instance<RegisterBlock>; + +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + #[doc = "Module Control Register 0"] + pub MCR0: u32, + #[doc = "Module Control Register 1"] + pub MCR1: u32, + #[doc = "Module Control Register 2"] + pub MCR2: u32, + #[doc = "AHB Bus Control Register"] + pub AHBCR: u32, + #[doc = "Interrupt Enable Register"] + pub INTEN: u32, + #[doc = "Interrupt Register"] + pub INTR: u32, + #[doc = "LUT Key Register"] + pub LUTKEY: u32, + #[doc = "LUT Control Register"] + pub LUTCR: u32, + + #[doc = "AHB RX Buffer Control Register 0"] + pub AHBRXBUFCR0: [u32; 8], + _reserved0: [u8; 0x20], + + #[doc = "Flash Control Register 0"] + pub FLSHCR0: [u32; 4], + #[doc = "Flash Control Register 1"] + pub FLSHCR1: [u32; 4], + #[doc = "Flash Control Register 2"] + pub FLSHCR2: [u32; 4], + _reserved1: [u8; 4], + pub FLSHCR4: u32, + _reserved2: [u8; 0x08], + + #[doc = "IP Control Register 0"] + pub IPCR0: u32, + #[doc = "IP Control Register 1"] + pub IPCR1: u32, + _reserved3: [u8; 0x08], + #[doc = "IP Command Register"] + pub IPCMD: u32, + _reserved4: [u8; 0x04], + #[doc = "IP RX FIFO Control Register"] + pub IPRXFCR: u32, + #[doc = "IP TX FIFO Control Register"] + pub IPTXFCR: u32, + #[doc = "DLL Control Register 0"] + pub DLLCR: [u32; 2usize], + _reserved5: [u8; 0x18], + #[doc = "Status Register 0"] + pub STS0: u32, + #[doc = "Status Register 1"] + pub STS1: u32, + #[doc = "Status Register 2"] + pub STS2: u32, + #[doc = "AHB Suspend Status Register"] + pub AHBSPNDSTS: u32, + #[doc = "IP RX FIFO Status Register"] + pub IPRXFSTS: u32, + #[doc = "IP TX FIFO Status Register"] + pub IPTXFSTS: u32, + _reserved6: [u8; 0x08], + #[doc = "IP RX FIFO Data Register 0"] + pub RFDR: [u32; 32usize], + #[doc = "IP TX FIFO Data Register 0"] + pub TFDR: [u32; 32usize], + #[doc = "LUT 0"] + pub LUT: [u32; 64usize], +} + +/// Port A, device 1. +pub const A1: usize = 0; +/// Port A, device 2. +pub const A2: usize = 1; +/// Port B, device 1. +pub const B1: usize = 2; +/// Port B, device 2. +pub const B2: usize = 3; + +/// Port A DLL access. +pub const DLL_A: usize = 0; +/// Port B DLL access. +pub const DLL_B: usize = 1; + +/// A FlexSPI instruction opcode. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct Opcode(pub u8); + +/// Stop execution, deassert CS. Next command sequence +/// (to the same flash device) will started from instruction pointer 0. +pub const STOP: Opcode = Opcode(0x00); + +/// Stop execution, deassert CS and save the operand +/// as the instruction start pointer for next sequence. +/// +/// Normally this instruction is used to support XIP enhance mode. +pub const JUMP_ON_CS: Opcode = Opcode(0x1F); + +/// Transmit command code to flash +pub const SDR_CMD: Opcode = Opcode(0x01); +/// Transmit row address to flash +pub const SDR_RADDR: Opcode = Opcode(0x02); +/// Transmit column address to flash +pub const SDR_CADDR: Opcode = Opcode(0x03); +/// Transmit mode bits to flash +/// +/// Bit number 1 +pub const SDR_MODE1: Opcode = Opcode(0x04); +/// Transmit mode bits to flash +/// +/// Bit number 2 +pub const SDR_MODE2: Opcode = Opcode(0x05); +/// Transmit mode bits to flash +/// +/// Bit number 4 +pub const SDR_MODE4: Opcode = Opcode(0x06); +/// Transmit mode bits to flash +/// +/// Bit number 8 +pub const SDR_MODE8: Opcode = Opcode(0x07); +/// Transmit programming data to flash +pub const SDR_WRITE: Opcode = Opcode(0x08); +/// Receive data from flash +/// +/// Read Data is put into AHB_RX_BUF or IP_RX_FIFO. +pub const SDR_READ: Opcode = Opcode(0x09); +/// Receive Read Data or Preamble bit from Flash device +/// +/// FlexSPI Controller will compare the data line bits with DLPR +/// register to determine a correct sampling clock phase. +pub const SDR_LEARN: Opcode = Opcode(0x0A); +/// Transmit Read/ Program Data size (byte number) to Flash +pub const SDR_DATASZ: Opcode = Opcode(0x0B); +/// Leave data lines undriven by FlexSPI controller. +/// +/// Provide turnaround cycles from host driving to device driving. +/// `num_pads` will determine the number of pads in input mode. +pub const SDR_DUMMY: Opcode = Opcode(0x0C); +/// Similar to `DUMMY`, but the cycle number is different +pub const SDR_DUMMY_RWDS: Opcode = Opcode(0x0D); + +/// Turn an SDR opcode into a DDR opcode. +const fn sdr_to_ddr(opcode: Opcode) -> Opcode { + Opcode(opcode.0 + 0x20) +} + +/// Transmit command code to flash +pub const DDR_CMD: Opcode = sdr_to_ddr(SDR_CMD); +/// Transmit row address to flash +pub const DDR_RADDR: Opcode = sdr_to_ddr(SDR_RADDR); +/// Transmit column address to flash +pub const DDR_CADDR: Opcode = sdr_to_ddr(SDR_CADDR); +/// Transmit mode bits to flash +/// +/// Bit number 1 +pub const DDR_MODE1: Opcode = sdr_to_ddr(SDR_MODE1); +/// Transmit mode bits to flash +/// +/// Bit number 2 +pub const DDR_MODE2: Opcode = sdr_to_ddr(SDR_MODE2); +/// Transmit mode bits to flash +/// +/// Bit number 4 +pub const DDR_MODE4: Opcode = sdr_to_ddr(SDR_MODE4); +/// Transmit mode bits to flash +/// +/// Bit number 8 +pub const DDR_MODE8: Opcode = sdr_to_ddr(SDR_MODE8); +/// Transmit programming data to flash +pub const DDR_WRITE: Opcode = sdr_to_ddr(SDR_WRITE); +/// Receive data from flash +/// +/// Read Data is put into AHB_RX_BUF or IP_RX_FIFO. +pub const DDR_READ: Opcode = sdr_to_ddr(SDR_READ); +/// Receive Read Data or Preamble bit from Flash device +/// +/// FlexSPI Controller will compare the data line bits with DLPR +/// register to determine a correct sampling clock phase. +pub const DDR_LEARN: Opcode = sdr_to_ddr(SDR_LEARN); +/// Transmit Read/ Program Data size (byte number) to Flash +pub const DDR_DATASZ: Opcode = sdr_to_ddr(SDR_DATASZ); +/// Leave data lines undriven by FlexSPI controller. +/// +/// Provide turnaround cycles from host driving to device driving. +/// `num_pads` will determine the number of pads in input mode. +pub const DDR_DUMMY: Opcode = sdr_to_ddr(SDR_DUMMY); +/// Similar to `DUMMY`, but the cycle number is different +pub const DDR_DUMMY_RWDS: Opcode = sdr_to_ddr(SDR_DUMMY_RWDS); + +/// Number of pads to use to execute the instruction +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum Pads { + /// Single mode + One = 0x00, + /// Dual mode + Two = 0x01, + /// Quad mode + Four = 0x02, + /// Octal mode + Eight = 0x03, +} + +/// FlexSPI LUT instruction. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct Instr(u16); + +impl Instr { + /// Form a new instruction. + #[must_use] + pub const fn new(opcode: Opcode, pads: Pads, operand: u8) -> Self { + let opcode = (opcode.0 as u16) << 10; + let pads = (pads as u16) << 8; + let operand = operand as u16; + Self(opcode | pads | operand) + } + + /// Convenience for a `STOP` instruction. + pub const STOP: Self = Instr::new(STOP, Pads::One, 0); + /// Convenience for a `JUMP_ON_CS` instruction. + pub const JUMP_ON_CS: Self = Self::new(JUMP_ON_CS, Pads::One, 0); +} + +/// A FlexSPI LUT instruction sequence. +#[repr(align(4))] +pub struct Sequence(pub [Instr; 8]); + +const _: () = assert!(size_of::<Sequence>() == size_of::<[u32; 4]>()); + +/// The maximum number of sequences that can +/// be executed per IP command. +/// +/// Besides respecting this constraing, you also +/// must remain in bounds of the lookup table. +pub const MAX_SEQ_PER_IP_CMD: usize = 8; + +/// The number of sequences in the lookup table. +pub const LUT_SIZE: usize = 16; + +/// A sequence identifier. +/// +/// Describes the location of the sequence in the +/// LUT. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(usize)] +pub enum SeqId { + Seq00, + Seq01, + Seq02, + Seq03, + Seq04, + Seq05, + Seq06, + Seq07, + Seq08, + Seq09, + Seq10, + Seq11, + Seq12, + Seq13, + Seq14, + Seq15, +} + +impl SeqId { + /// Produce the next sequence ID. + /// + /// Returns `None` if the next ID is invalid. + pub const fn next_id(self) -> Option<Self> { + // Safety: raw value remains in bounds of enum + // representation. + unsafe { + let raw = (self as usize) + 1; + if raw < 16 { + Some(core::mem::transmute(raw)) + } else { + None + } + } + } +} + +/// An iterator over sequence IDs. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SeqIdRange(Option<SeqId>); + +impl SeqIdRange { + /// Returns a sequence ID range with no values. + pub const fn empty() -> Self { + Self(None) + } + /// Returns a range starting from the given ID. + pub const fn start(seq_id: SeqId) -> Self { + Self(Some(seq_id)) + } +} + +impl Iterator for SeqIdRange { + type Item = SeqId; + fn next(&mut self) -> Option<Self::Item> { + let this = self.0?; + let next = this.next_id(); + self.0 = next; + Some(this) + } +} + +/// Install an instruction sequence into the FlexSPI LUT. +/// +/// The implementation assumes that you've already unlocked the LUT. +pub fn set_sequence(flexspi: Instance, sequence_id: SeqId, sequence: &Sequence) { + // Safety: see inline notes. + unsafe { + // The LUT is a contiguous collection of sequences, and each + // sequence is a contiguous collection of instructions. + let lut: *mut [u32; 64] = (&raw mut (*flexspi.as_ptr()).LUT); + let lut: *mut Sequence = lut.cast(); + + // We remain in bounds of the LUT, even if the MMIO + // allocation supports more sequences. + let lut: *mut Sequence = lut.add(sequence_id as usize); + + // 8 instructions per sequence, and each instruction + // is a u16. We can treat that as four u32s on this + // target. Sequence is four-byte aligned for u32 access. + let lut: *mut u32 = lut.cast(); + let seq: *const u32 = (sequence as *const Sequence).cast(); + + // Accesses remain in bounds of the caller's sequence + // and LUT's sequence. + lut.add(0).write_volatile(seq.add(0).read()); + lut.add(1).write_volatile(seq.add(1).read()); + lut.add(2).write_volatile(seq.add(2).read()); + lut.add(3).write_volatile(seq.add(3).read()); + } +} + +/// Unlock the LUT before installing sequences. +#[inline] +pub fn unlock_lut(flexspi: Instance) { + ral_registers::write_reg!(self, flexspi, LUTKEY, LUTKEY_MAGIC); + ral_registers::write_reg!(self, flexspi, LUTCR, UNLOCK: 1, LOCK: 0); +} + +/// Lock the LUT after installing sequences. +#[inline] +pub fn lock_lut(flexspi: Instance) { + ral_registers::write_reg!(self, flexspi, LUTKEY, LUTKEY_MAGIC); + ral_registers::write_reg!(self, flexspi, LUTCR, UNLOCK: 0, LOCK: 1); +} + +ral_registers::register! { + #[doc = "Module Control Register 0"] + pub MCR0<u32> RW [ + #[doc = "Software Reset"] + SWRESET start(0) width(1) RW {} + #[doc = "Module Disable"] + MDIS start(1) width(1) RW {} + #[doc = "Sample Clock source selection for Flash Reading"] + RXCLKSRC start(4) width(2) RW { + #[doc = "Dummy Read strobe generated by FlexSPI Controller and loopback internally."] + LOOPBACK_INTERNAL = 0, + #[doc = "Dummy Read strobe generated by FlexSPI Controller and loopback from DQS pad."] + LOOPBACK_DQS = 0x1, + #[doc = "Flash provided Read strobe and input from DQS pad"] + FLASH_DQS = 0x3, + } + #[doc = "Enable AHB bus Read Access to IP RX FIFO."] + ARDFEN start(6) width(1) RW { + #[doc = "IP RX FIFO should be read by IP Bus. AHB Bus read access to IP RX FIFO memory space will get bus error response."] + IP_BUS = 0, + #[doc = "IP RX FIFO should be read by AHB Bus. IP Bus read access to IP RX FIFO memory space will always return data zero but no bus error response."] + AHB_BUS = 0x1, + } + #[doc = "Enable AHB bus Write Access to IP TX FIFO."] + ATDFEN start(7) width(1) RW { + #[doc = "IP TX FIFO should be written by IP Bus. AHB Bus write access to IP TX FIFO memory space will get bus error response."] + IP_BUS = 0, + #[doc = "IP TX FIFO should be written by AHB Bus. IP Bus write access to IP TX FIFO memory space will be ignored but no bus error response."] + AHB_BUS = 0x1, + } + #[doc = "The serial root clock could be divided inside FlexSPI . Refer Clocks chapter for more details on clocking."] + SERCLKDIV start(8) width(3) RW { + #[doc = "Divided by 1"] + DIVIDE_1 = 0, + #[doc = "Divided by 2"] + DIVIDE_2 = 0x1, + #[doc = "Divided by 3"] + DIVIDE_3 = 0x2, + #[doc = "Divided by 4"] + DIVIDE_4 = 0x3, + #[doc = "Divided by 5"] + DIVIDE_5 = 0x4, + #[doc = "Divided by 6"] + DIVIDE_6 = 0x5, + #[doc = "Divided by 7"] + DIVIDE_7 = 0x6, + #[doc = "Divided by 8"] + DIVIDE_8 = 0x7, + } + #[doc = "Half Speed Serial Flash access Enable."] + HSEN start(11) width(1) RW {} + #[doc = "Doze mode enable bit"] + DOZEEN start(12) width(1) RW {} + #[doc = "This bit is to support Flash Octal mode access by combining Port A and B Data pins (A_DATA[3:0] and B_DATA[3:0]), when Port A and Port B are of 4 bit data width."] + COMBINATIONEN start(13) width(1) RW {} + #[doc = "This bit is used to force SCLK output free-running. For FPGA applications, external device may use SCLK as reference clock to its internal PLL. If SCLK free-running is enabled, data sampling with loopback clock from SCLK pad is not supported (MCR0[RXCLKSRC]=2)."] + SCKFREERUNEN start(14) width(1) RW {} + #[doc = "Time out wait cycle for IP command grant."] + IPGRANTWAIT start(16) width(8) RW {} + #[doc = "Timeout wait cycle for AHB command grant."] + AHBGRANTWAIT start(24) width(8) RW {} + ] +} + +ral_registers::register! { + #[doc = "Module Control Register 1"] + pub MCR1<u32> RW [ + #[doc = "AHB Read/Write access to Serial Flash Memory space will timeout if not data received from Flash or data not transmitted after AHBBUSWAIT * 1024 ahb clock cycles, AHB Bus will get an error response"] + AHBBUSWAIT start(0) width(16) RW {} + #[doc = "Command Sequence Execution will timeout and abort after SEQWAIT * 1024 Serial Root Clock cycles"] + SEQWAIT start(16) width(16) RW {} + ] +} + +ral_registers::register! { + #[doc = "Module Control Register 2"] + pub MCR2<u32> RW [ + #[doc = "This bit determines whether AHB RX Buffer and AHB TX Buffer will be cleaned automatically when FlexSPI returns STOP mode ACK. Software should set this bit if AHB RX Buffer or AHB TX Buffer will be powered off in STOP mode. Otherwise AHB read access after exiting STOP mode may hit AHB RX Buffer or AHB TX Buffer but their data entries are invalid."] + CLRAHBBUFOPT start(11) width(1) RW {} + #[doc = "All external devices are same devices (both in types and size) for A1/A2/B1/B2."] + SAMEDEVICEEN start(15) width(1) RW {} + #[doc = "B_SCLK pad can be used as A_SCLK differential clock output (inverted clock to A_SCLK). In this case, port B flash access is not available. After changing the value of this field, MCR0[SWRESET] should be set."] + SCKBDIFFOPT start(19) width(1) RW {} + #[doc = "Wait cycle (in AHB clock cycle) for idle state before suspended command sequence resumed."] + RESUMEWAIT start(24) width(8) RW {} + ] +} + +ral_registers::register! { + #[doc = "AHB Bus Control Register"] + pub AHBCR<u32> RW [ + #[doc = "Parallel mode enabled for AHB triggered Command (both read and write) ."] + APAREN start(0) width(1) RW {} + #[doc = "Clear the status/pointers of AHB RX Buffer. Auto-cleared."] + CLRAHBRXBUF start(1) width(1) RW {} + #[doc = "Enable AHB bus cachable read access support."] + CACHABLEEN start(3) width(1) RW {} + #[doc = "Enable AHB bus bufferable write access support. This field affects the last beat of AHB write access, refer for more details about AHB bufferable write."] + BUFFERABLEEN start(4) width(1) RW {} + #[doc = "AHB Read Prefetch Enable."] + PREFETCHEN start(5) width(1) RW {} + #[doc = "AHB Read Address option bit. This option bit is intend to remove AHB burst start address alignment limitation."] + READADDROPT start(6) width(1) RW {} + #[doc = "AHB Read Size Alignment"] + READSZALIGN start(10) width(1) RW {} + #[doc = "AHB Read ECC Enable"] + ECCEN start(11) width(1) RW {} + #[doc = "AHB transaction SPLIT"] + SPLITEN start(12) width(1) RW {} + #[doc = "AHB SPLIT SIZE"] + SPLIT_LIMIT start(13) width(2) RW { + #[doc = "AHB Split Size=8bytes"] + SPLIT_8 = 0, + #[doc = "AHB Split Size=16bytes"] + SPLIT_16 = 0x1, + #[doc = "AHB Split Size=32bytes"] + SPLIT_32 = 0x2, + #[doc = "AHB Split Size=64bytes"] + SPLIT_64 = 0x3, + } + #[doc = "OTFAD KEY BLOC ECC Enable"] + KEYECCEN start(15) width(1) RW {} + #[doc = "AHB ECC Single bit ERR CLR"] + ECCSINGLEERRCLR start(16) width(1) RW {} + #[doc = "AHB ECC Multi bits ERR CLR"] + ECCMULTIERRCLR start(17) width(1) RW {} + #[doc = "AHB Master ID Remapping enable"] + HMSTRIDREMAP start(18) width(1) RW {} + #[doc = "ECC Read data swap function"] + ECCSWAPEN start(19) width(1) RW {} + #[doc = "Decides all AHB read/write boundary. All access cross the boundary will be divided into smaller sub accesses."] + ALIGNMENT start(20) width(2) RW { + #[doc = "No limit"] + NO_LIMIT = 0, + #[doc = "1 KBytes"] + BYTES_1024 = 0x1, + #[doc = "512 Bytes"] + BYTES_512 = 0x2, + #[doc = "256 Bytes"] + BYTES_256 = 0x3, + } + ] +} + +/// Interrupt Enable Register. +#[allow(non_snake_case)] +pub mod INTEN { + pub use super::INTR::*; +} + +ral_registers::register! { + #[doc = "Interrupt Register"] + pub INTR<u32> RW [ + #[doc = "IP triggered Command Sequences Execution finished interrupt. This interrupt is also generated when there is IPCMDGE or IPCMDERR interrupt generated."] + IPCMDDONE start(0) width(1) RW {} + #[doc = "IP triggered Command Sequences Grant Timeout interrupt."] + IPCMDGE start(1) width(1) RW {} + #[doc = "AHB triggered Command Sequences Grant Timeout interrupt."] + AHBCMDGE start(2) width(1) RW {} + #[doc = "IP triggered Command Sequences Error Detected interrupt. When an error detected for IP command, this command will be ignored and not executed at all."] + IPCMDERR start(3) width(1) RW {} + #[doc = "AHB triggered Command Sequences Error Detected interrupt. When an error detected for AHB command, this command will be ignored and not executed at all."] + AHBCMDERR start(4) width(1) RW {} + #[doc = "IP RX FIFO watermark available interrupt."] + IPRXWA start(5) width(1) RW {} + #[doc = "IP TX FIFO watermark empty interrupt."] + IPTXWE start(6) width(1) RW {} + #[doc = "SCLK is stopped during command sequence because Async RX FIFO full interrupt."] + SCKSTOPBYRD start(8) width(1) RW {} + #[doc = "SCLK is stopped during command sequence because Async TX FIFO empty interrupt."] + SCKSTOPBYWR start(9) width(1) RW {} + #[doc = "AHB Bus timeout or AHB bus illegal access Flash during OTFAD key blob processing interrupt."] + AHBBUSERROR start(10) width(1) RW {} + #[doc = "Sequence execution timeout interrupt."] + SEQTIMEOUT start(11) width(1) RW {} + #[doc = "OTFAD key blob processing done interrupt."] + KEYDONE start(12) width(1) RW {} + #[doc = "OTFAD key blob processing error interrupt."] + KEYERROR start(13) width(1) RW {} + #[doc = "ECC multi bits error interrupt."] + ECCMULTIERR start(14) width(1) RW {} + #[doc = "ECC single bit error interrupt."] + ECCSINGLEERR start(15) width(1) RW {} + #[doc = "IP command security violation interrupt."] + IPCMDSECUREVIO start(16) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "LUT Key Register"] + pub LUTKEY<u32> RW [] +} + +pub const LUTKEY_MAGIC: u32 = 0x5AF05AF0; + +ral_registers::register! { + #[doc = "LUT Control Register"] + pub LUTCR<u32> RW [ + #[doc = "Lock LUT"] + LOCK start(0) width(1) RW {} + #[doc = "Unlock LUT"] + UNLOCK start(1) width(1) RW {} + #[doc = "LUT protection"] + PROTECT start(2) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "AHB RX Buffer Control Register 0"] + pub AHBRXBUFCR0<u32> RW [ + #[doc = "AHB RX Buffer Size in 64 bits."] + BUFSZ start(0) width(10) RW {} + #[doc = "This AHB RX Buffer is assigned according to AHB Master with ID (MSTR_ID)."] + MSTRID start(16) width(4) RW {} + #[doc = "This priority for AHB Master Read which this AHB RX Buffer is assigned. 7 is the highest priority, 0 the lowest."] + PRIORITY start(24) width(3) RW {} + #[doc = "AHB RX Buffer address region funciton enable"] + REGIONEN start(30) width(1) RW {} + #[doc = "AHB Read Prefetch Enable for current AHB RX Buffer corresponding Master."] + PREFETCHEN start(31) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "Flash Control Register 0"] + pub FLSHCR0<u32> RW [ + #[doc = "Flash Size in KByte."] + FLSHSZ start(0) width(23) RW {} + #[doc = "AHB write access split function control."] + SPLITWREN start(30) width(1) RW {} + #[doc = "AHB read access split function control."] + SPLITRDEN start(31) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "Flash Control Register 1"] + pub FLSHCR1<u32> RW [ + #[doc = "Serial Flash CS setup time."] + TCSS start(0) width(5) RW {} + #[doc = "Serial Flash CS Hold time."] + TCSH start(5) width(5) RW {} + #[doc = "Word Addressable."] + WA start(10) width(1) RW {} + #[doc = "Column Address Size."] + CAS start(11) width(4) RW {} + #[doc = "CS interval unit"] + CSINTERVALUNIT start(15) width(1) RW {} + #[doc = "This field is used to set the minimum interval between flash device Chip selection deassertion and flash device Chip selection assertion. If external flash has a limitation on the interval between command sequences, this field should be set accordingly. If there is no limitation, set this field with value 0x0."] + CSINTERVAL start(16) width(16) RW {} + ] +} + +ral_registers::register! { + #[doc = "Flash Control Register 2"] + pub FLSHCR2<u32> RW [ + #[doc = "Sequence Index for AHB Read triggered Command in LUT."] + ARDSEQID start(0) width(4) RW {} + #[doc = "Sequence Number for AHB Read triggered Command in LUT."] + ARDSEQNUM start(5) width(3) RW {} + #[doc = "Sequence Index for AHB Write triggered Command."] + AWRSEQID start(8) width(4) RW {} + #[doc = "Sequence Number for AHB Write triggered Command."] + AWRSEQNUM start(13) width(3) RW {} + #[doc = "For certain devices (such as FPGA), it need some time to write data into internal memory after the command sequences finished on FlexSPI interface"] + AWRWAIT start(16) width(12) RW {} + #[doc = "AWRWAIT unit"] + AWRWAITUNIT start(28) width(3) RW { + #[doc = "The AWRWAIT unit is 2 ahb clock cycle"] + AHB_CYCLE_2 = 0, + #[doc = "The AWRWAIT unit is 8 ahb clock cycle"] + AHB_CYCLE_8 = 1, + #[doc = "The AWRWAIT unit is 32 ahb clock cycle"] + AHB_CYCLE_32 = 2, + #[doc = "The AWRWAIT unit is 128 ahb clock cycle"] + AHB_CYCLE_128 = 3, + #[doc = "The AWRWAIT unit is 512 ahb clock cycle"] + AHB_CYCLE_512 = 4, + #[doc = "The AWRWAIT unit is 2048 ahb clock cycle"] + AHB_CYCLE_2048 = 5, + #[doc = "The AWRWAIT unit is 8192 ahb clock cycle"] + AHB_CYCLE_8192 = 6, + #[doc = "The AWRWAIT unit is 32768 ahb clock cycle"] + AHB_CYCLE_32768 = 7, + } + #[doc = "Clear the instruction pointer which is internally saved pointer by JMP_ON_CS. Refer Programmable Sequence Engine for details."] + CLRINSTRPTR start(31) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "Flash Control Register 4"] + pub FLSHCR4<u32> RW [ + #[doc = "Write mask option bit 1. This option bit could be used to remove AHB write burst start address alignment limitation."] + WMOPT1 start(0) width(1) RW {} + #[doc = "Write mask option bit 2. When using AP memory, This option bit could be used to remove AHB write burst minimal length limitation. When using this bit, WMOPT1 should also be set."] + WMOPT2 start(1) width(1) RW {} + #[doc = "Write mask enable bit for flash device on port A. When write mask function is needed for memory device on port A, this bit must be set."] + WMENA start(2) width(1) RW {} + #[doc = "Write mask enable bit for flash device on port B. When write mask function is needed for memory device on port B, this bit must be set."] + WMENB start(3) width(1) RW {} + #[doc = "Enable APMEM 16 bit write mask function, bit 9 for A1-B1 pair, bit 10 for A2-B2 pair."] + PAR_WM start(9) width(2) RW {} + #[doc = "Disable the address shift logic for lower density of 16 bit PSRAM."] + PAR_ADDR_ADJ_DIS start(11) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "IP Control Register 0"] + pub IPCR0<u32> RW [] +} + +ral_registers::register! { + #[doc = "IP Control Register 1"] + pub IPCR1<u32> RW [ + #[doc = "Flash Read/Program Data Size (in Bytes) for IP command."] + IDATSZ start(0) width(16) RW {} + #[doc = "Sequence Index in LUT for IP command."] + ISEQID start(16) width(4) RW {} + #[doc = "Sequence Number for IP command. Zero is the first sequence."] + ISEQNUM start(24) width(3) RW {} + #[doc = "Parallel mode Enabled for IP command."] + IPAREN start(31) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "IP Command Register"] + pub IPCMD<u32> RW [ + #[doc = "Setting this bit will trigger an IP Command."] + TRG start(0) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "Status Register 0"] + pub STS0<u32> RO [ + #[doc = "This status bit indicates the state machine in SEQ_CTL is idle and there is command sequence executing on FlexSPI interface."] + SEQIDLE start(0) width(1) RO {} + #[doc = "This status bit indicates the state machine in ARB_CTL is busy and there is command sequence granted by arbitrator and not finished yet on FlexSPI interface. When ARB_CTL state (ARBIDLE=0x1) is idle, there will be no transaction on FlexSPI interface also (SEQIDLE=0x1). So this bit should be polled to wait for FlexSPI controller become idle instead of SEQIDLE."] + ARBIDLE start(1) width(1) RO {} + #[doc = "This status field indicates the trigger source of current command sequence granted by arbitrator. This field value is meaningless when ARB_CTL is not busy (STS0[ARBIDLE]=0x1)."] + ARBCMDSRC start(2) width(2) RO { + #[doc = "Triggered by AHB read command (triggered by AHB read)."] + AHB_READ = 0, + #[doc = "Triggered by AHB write command (triggered by AHB Write)."] + AHB_WRITE = 1, + #[doc = "Triggered by IP command (triggered by setting register bit IPCMD.TRG)."] + IP_COMMAND = 2, + #[doc = "Triggered by suspended command (resumed)."] + SUSPEND = 3, + } + ] +} + +ral_registers::register! { + #[doc = "Status Register 1"] + pub STS1<u32> RO [ + #[doc = "Indicates the sequence index when an AHB command error is detected. This field will be cleared when INTR[AHBCMDERR] is write-1-clear(w1c)."] + AHBCMDERRID start(0) width(4) RO {} + #[doc = "Indicates the Error Code when AHB command Error detected. This field will be cleared when INTR[AHBCMDERR] is write-1-clear(w1c)."] + AHBCMDERRCODE start(8) width(4) RO { + #[doc = "No error."] + NONE = 0, + #[doc = "AHB Write command with JMP_ON_CS instruction used in the sequence."] + WRITE_WITH_JUMP_ON_CS = 2, + #[doc = "There is unknown instruction opcode in the sequence."] + UNKNOWN_OPCODE = 3, + #[doc = "Instruction DUMMY_SDR/DUMMY_RWDS_SDR used in DDR sequence."] + DUMMY_SDR_IN_DDR = 4, + #[doc = "Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence."] + DUMMY_DDR_IN_SDR = 5, + #[doc = "Sequence execution timeout."] + TIMEOUT = 14, + } + #[doc = "Indicates the sequence Index when IP command error detected. This field will be cleared when INTR[IPCMDERR] is write-1-clear(w1c)."] + IPCMDERRID start(16) width(4) RO {} + #[doc = "Indicates the Error Code when IP command Error detected. This field will be cleared when INTR[IPCMDERR] is write-1-clear(w1c)."] + IPCMDERRCODE start(24) width(4) RO { + #[doc = "No error."] + NONE = 0, + #[doc = "IP command with JMP_ON_CS instruction used in the sequence."] + JUMP_ON_CS = 2, + #[doc = "There is unknown instruction opcode in the sequence."] + UNKNOWN_OPCODE = 3, + #[doc = "Instruction DUMMY_SDR/DUMMY_RWDS_SDR used in DDR sequence."] + DUMMY_SDR_IN_DDR = 4, + #[doc = "Instruction DUMMY_DDR/DUMMY_RWDS_DDR used in SDR sequence."] + DUMMY_DDR_IN_SDR = 5, + #[doc = "Flash access start address exceed the whole flash address range (A1/A2/B1/B2)."] + OUT_OF_RANGE = 6, + #[doc = "Sequence execution timeout."] + TIMEOUT = 14, + #[doc = "Flash boundary crossed."] + BOUNDARY_CROSS = 15, + } + ] +} + +ral_registers::register! { + #[doc = "Status Register 2"] + pub STS2<u32> RO [ + #[doc = "Flash A sample clock slave delay line locked."] + ASLVLOCK start(0) width(1) RO {} + #[doc = "Flash A sample clock reference delay line locked."] + AREFLOCK start(1) width(1) RO {} + #[doc = "Flash A sample clock slave delay line delay cell number selection ."] + ASLVSEL start(2) width(6) RO {} + #[doc = "Flash A sample clock reference delay line delay cell number selection."] + AREFSEL start(8) width(6) RO {} + #[doc = "Flash B sample clock slave delay line locked."] + BSLVLOCK start(16) width(1) RO {} + #[doc = "Flash B sample clock reference delay line locked."] + BREFLOCK start(17) width(1) RO {} + #[doc = "Flash B sample clock slave delay line delay cell number selection."] + BSLVSEL start(18) width(6) RO {} + #[doc = "Flash B sample clock reference delay line delay cell number selection."] + BREFSEL start(24) width(6) RO {} + ] +} + +ral_registers::register! { + #[doc = "IP RX FIFO Status Register"] + pub IPRXFSTS<u32> RO [ + #[doc = "Fill level of IP RX FIFO."] + FILL start(0) width(8) RO {} + #[doc = "Total Read Data Counter: RDCNTR * 64 Bits."] + RDCNTR start(16) width(16) RO {} + ] +} + +ral_registers::register! { + #[doc = "IP TX FIFO Status Register"] + pub IPTXFSTS<u32> RO [ + #[doc = "Fill level of IP TX FIFO."] + FILL start(0) width(8) RO {} + #[doc = "Total Write Data Counter: WRCNTR * 64 Bits."] + WRCNTR start(16) width(16) RO {} + ] +} + +ral_registers::register! { + #[doc = "IP RX FIFO Control Register"] + pub IPRXFCR<u32> RW [ + #[doc = "Clear all valid data entries in IP RX FIFO."] + CLRIPRXF start(0) width(1) RW {} + #[doc = "IP RX FIFO reading by DMA enabled."] + RXDMAEN start(1) width(1) RW { + #[doc = "IP RX FIFO would be read by processor."] + CPU = 0, + #[doc = "IP RX FIFO would be read by DMA."] + DMA = 1, + } + #[doc = "Watermark level is (RXWMRK+1)*64 Bits."] + RXWMRK start(2) width(5) RW {} + ] +} + +ral_registers::register! { + #[doc = "IP TX FIFO Control Register"] + pub IPTXFCR<u32> RW [ + #[doc = "Clear all valid data entries in IP TX FIFO."] + CLRIPTXF start(0) width(1) RW {} + #[doc = "IP TX FIFO filling by DMA enabled."] + TXDMAEN start(1) width(1) RW { + #[doc = "IP TX FIFO would be filled by processor."] + CPU = 0, + #[doc = "IP TX FIFO would be filled by DMA."] + DMA = 1, + } + #[doc = "Watermark level is (TXWMRK+1)*64 Bits."] + TXWMRK start(2) width(5) RW {} + ] +} + +ral_registers::register! { + pub RFDR<u32> RO [] +} + +ral_registers::register! { + pub TFDR<u32> WO [] +} + +#[cfg(test)] +mod tests { + use core::mem::MaybeUninit; + + use ral_registers::Instance; + + use crate::{Instr, SeqId, SeqIdRange, Sequence}; + + use super::RegisterBlock; + + #[test] + fn layout_test() { + use core::mem::offset_of; + assert_eq!(offset_of!(RegisterBlock, FLSHCR4), 0x94); + assert_eq!(offset_of!(RegisterBlock, IPCR0), 0xA0); + assert_eq!(offset_of!(RegisterBlock, STS0), 0xE0); + assert_eq!(offset_of!(RegisterBlock, AHBSPNDSTS), 0xEC); + assert_eq!(offset_of!(RegisterBlock, RFDR), 0x100); + assert_eq!(offset_of!(RegisterBlock, TFDR), 0x180); + assert_eq!(offset_of!(RegisterBlock, LUT), 0x200); + } + + #[test] + fn set_sequence() { + let mut flexspi: RegisterBlock = unsafe { MaybeUninit::zeroed().assume_init() }; + let flexspi = unsafe { Instance::new_unchecked(&mut flexspi) }; + super::set_sequence(flexspi, SeqId::Seq04, &Sequence([Instr::STOP; _])); + } + + #[test] + fn seq_id_start_range() { + let mut range = SeqIdRange::start(SeqId::Seq13); + assert_eq!(range.next(), Some(SeqId::Seq13)); + assert_eq!(range.next(), Some(SeqId::Seq14)); + assert_eq!(range.next(), Some(SeqId::Seq15)); + assert_eq!(range.next(), None); + assert_eq!(range.next(), None); + assert_eq!(range.next(), None); + assert_eq!(range.next(), None); + } +} |
