flash: fix flash read

This commit is contained in:
occheung 2020-10-06 15:03:53 +08:00
parent 7e09318919
commit 52986e214f
3 changed files with 158 additions and 140 deletions

View File

@ -1,91 +1,64 @@
use embedded_hal::{
digital::v2::{OutputPin, InputPin},
blocking::spi::Transfer,
blocking::delay::DelayUs,
};
use heapless::{String, consts::*};
use smoltcp as net;
use embedded_nal as nal;
use core::str::FromStr;
#[derive(Debug)]
pub enum FPGAFlashError {
SPICommunicationError,
NegotiationError,
ResetStatusError,
unsafe fn read_record_length(addr: u32) -> u32 {
core::ptr::read(addr as *const u32)
}
const DATA: &'static [u8] = include_bytes!("../build/top.bin");
unsafe fn read_byte(addr: u32) -> u8 {
core::ptr::read(addr as *const u8)
}
// A public method to flash iCE40 FPGA on Humpback
pub fn flash_ice40_fpga<SPI: Transfer<u8>,
SS: OutputPin,
RST: OutputPin,
DELAY: DelayUs<u32>,
DONE: InputPin>
(mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError>
fn read_flash_str(addr: &mut u32) -> (String<U32>, String<U64>) {
let mut key: String<U32> = String::new();
let mut string: String<U64> = String::new();
let record_length = unsafe {
read_record_length(*addr)
};
if record_length == 0xFFFFFFFF {
return (key, string);
}
let mut key_string_div = false;
for f_addr in (*addr)+4..((*addr)+record_length) {
if key_string_div {
string.push(unsafe {read_byte(f_addr)} as char).unwrap();
} else {
let c = unsafe {read_byte(f_addr)};
if c == 0 {
key_string_div = true;
} else {
key.push(c as char).unwrap();
}
}
}
*addr += record_length;
(key, string)
}
pub fn read_flash(addr: &mut u32)
-> (net::wire::IpCidr, net::wire::EthernetAddress, nal::Ipv4Addr, String<U64>)
{
// Data buffer setup
let mut dummy_byte :[u8; 1] = [0x00];
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
// Drive CRESET_B low
creset.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least 200ns
delay.delay_us(1_u32);
// Drive CRESET_B high
creset.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least another 1200us to clear internal config memory
delay.delay_us(1200_u32);
// Before data transmission starts, check if C_DONE is truly low
// If C_DONE is high, the FPGA reset procedure is unsuccessful
match cdone.is_low() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Set SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send 8 dummy clock, effectively 1 byte of 0x00
spi.transfer(&mut dummy_byte)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send the whole image without interruption
for byte in DATA.into_iter() {
let mut single_byte_slice = [*byte];
spi.transfer(&mut single_byte_slice)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
let mut cidr = net::wire::IpCidr::Ipv6(
net::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX,
);
let mut eth = net::wire::EthernetAddress::BROADCAST;
let mut ip = nal::Ipv4Addr::unspecified();
let mut name = String::new();
loop {
let (key, string) = read_flash_str(addr);
if key.len() == 0 {
return (cidr, eth, ip, name);
}
match key.as_str() {
"CIDR" => cidr = net::wire::IpCidr::from_str(&string).unwrap(),
"MAC" => eth = net::wire::EthernetAddress::from_str(&string).unwrap(),
"BrokerIP" => ip = nal::Ipv4Addr::from_str(&string).unwrap(),
"Name" => name = string,
_ => {},
}
}
// Drive SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send at another 100 dummy clocks (choosing 13 bytes)
spi.transfer(&mut dummy_13_bytes)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Check the CDONE output from FPGA
// CDONE needs to be high
match cdone.is_high() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
spi.transfer(&mut dummy_13_bytes).map_err(|_| FPGAFlashError::SPICommunicationError)?;
Ok(())
}

91
src/fpga.rs Normal file
View File

@ -0,0 +1,91 @@
use embedded_hal::{
digital::v2::{OutputPin, InputPin},
blocking::spi::Transfer,
blocking::delay::DelayUs,
};
#[derive(Debug)]
pub enum FPGAFlashError {
SPICommunicationError,
NegotiationError,
ResetStatusError,
}
const DATA: &'static [u8] = include_bytes!("../build/top.bin");
// A public method to flash iCE40 FPGA on Humpback
pub fn flash_ice40_fpga<SPI: Transfer<u8>,
SS: OutputPin,
RST: OutputPin,
DELAY: DelayUs<u32>,
DONE: InputPin>
(mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError>
{
// Data buffer setup
let mut dummy_byte :[u8; 1] = [0x00];
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
// Drive CRESET_B low
creset.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least 200ns
delay.delay_us(1_u32);
// Drive CRESET_B high
creset.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Wait at least another 1200us to clear internal config memory
delay.delay_us(1200_u32);
// Before data transmission starts, check if C_DONE is truly low
// If C_DONE is high, the FPGA reset procedure is unsuccessful
match cdone.is_low() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Set SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send 8 dummy clock, effectively 1 byte of 0x00
spi.transfer(&mut dummy_byte)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Drive SPI_SS_B low
ss.set_low()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send the whole image without interruption
for byte in DATA.into_iter() {
let mut single_byte_slice = [*byte];
spi.transfer(&mut single_byte_slice)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
}
// Drive SPI_SS_B high
ss.set_high()
.map_err(|_| FPGAFlashError::NegotiationError)?;
// Send at another 100 dummy clocks (choosing 13 bytes)
spi.transfer(&mut dummy_13_bytes)
.map_err(|_| FPGAFlashError::SPICommunicationError)?;
// Check the CDONE output from FPGA
// CDONE needs to be high
match cdone.is_high() {
Ok(true) => {},
_ => return Err(FPGAFlashError::ResetStatusError),
};
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
spi.transfer(&mut dummy_13_bytes).map_err(|_| FPGAFlashError::SPICommunicationError)?;
Ok(())
}

View File

@ -10,7 +10,7 @@ use stm32h7xx_hal::ethernet;
use smoltcp as net;
use minimq::{
embedded_nal::{ IpAddr, Ipv4Addr },
embedded_nal::IpAddr,
MqttClient, QoS
};
@ -20,8 +20,6 @@ use rtic::cyccnt::{Instant, U32Ext};
use heapless::{ String, consts, consts::* };
use core::convert::TryInto;
#[macro_use]
pub mod bitmask_macro;
pub mod spi_slave;
@ -32,12 +30,14 @@ pub mod attenuator;
pub mod dds;
pub mod nal_tcp_client;
use crate::nal_tcp_client::{ NetStorage, NetworkStack };
pub mod flash;
use crate::flash::flash_ice40_fpga;
pub mod fpga;
use crate::fpga::flash_ice40_fpga;
pub mod mqtt_mux;
use crate::mqtt_mux::MqttMux;
pub mod urukul;
use crate::urukul::Urukul;
pub mod flash;
use crate::flash::read_flash;
mod logger;
@ -104,6 +104,12 @@ fn main() -> ! {
cp.DWT.enable_cycle_counter();
// Acquire client/broker IP Address, client MAC address from flash memory
let (ipv4_addr_cidr, mac_addr, broker_ipv4_addr, device_name) = {
let mut addr = 0x08100000;
read_flash(&mut addr)
};
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);
@ -112,58 +118,6 @@ fn main() -> ! {
let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF);
let gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG);
// Acquire client/broker IP Address, client MAC address from flash memory
let ipv4_addr_cidr = unsafe {
let ipv4_bits = core::ptr::read(0x08100000 as *const u64);
net::wire::IpCidr::new(
net::wire::IpAddress::v4(
((ipv4_bits >> 32) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 24) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 16) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 8) & 0xFF).try_into().unwrap()
),
((ipv4_bits >> 0) & 0xFF).try_into().unwrap()
)
};
let mac_addr = unsafe {
let mac_bits = core::ptr::read(0x08100020 as *const u64);
net::wire::EthernetAddress([
((mac_bits >> 40) & 0xFF).try_into().unwrap(),
((mac_bits >> 32) & 0xFF).try_into().unwrap(),
((mac_bits >> 24) & 0xFF).try_into().unwrap(),
((mac_bits >> 16) & 0xFF).try_into().unwrap(),
((mac_bits >> 8) & 0xFF).try_into().unwrap(),
(mac_bits & 0xFF).try_into().unwrap(),
])
};
let broker_ipv4_addr = unsafe {
let ipv4_bits = core::ptr::read(0x08100040 as *const u64);
Ipv4Addr::new(
((ipv4_bits >> 24) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 16) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 8) & 0xFF).try_into().unwrap(),
((ipv4_bits >> 0) & 0xFF).try_into().unwrap()
)
};
let device_name: String<U32> = unsafe {
let mut name = String::new();
let mut addr = 0x08100060;
loop {
let c = core::ptr::read(addr as *const u8);
if c == 4 {
break;
} else {
name.push(c as char).unwrap();
addr += 1;
}
}
name
};
// Note: ITM doesn't work beyond this, due to a pin conflict between:
// - FPGA_SPI: SCK (af5)
// - ST_LINK SWO (af0)