successfully wrote data
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
05230aa6c4
commit
e88650850b
84
Cargo.lock
generated
84
Cargo.lock
generated
@ -1,9 +1,51 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ftdi-prog"
|
name = "ftdi-prog"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
"ftdi-vcp-rs",
|
"ftdi-vcp-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -21,6 +63,48 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -8,5 +8,7 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ftdi-vcp-rs = { path = "ftdi-vcp-rs" }
|
ftdi-vcp-rs = { path = "ftdi-vcp-rs" }
|
||||||
|
clap = "*"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
# cc = { version = "1.0", features = ["parallel"] }
|
# cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
use ftdi_vcp_sys::{
|
use ftdi_vcp_sys::{
|
||||||
FT_Close, FT_GetBitMode, FT_GetComPortNumber, FT_OpenEx, FT_Purge, FT_ResetDevice,
|
FT_Close, FT_GetBitMode, FT_GetComPortNumber, FT_GetLatencyTimer, FT_OpenEx, FT_Purge, FT_Read,
|
||||||
FT_SetBitMode, FT_Write, DWORD, FT_HANDLE, FT_OPEN_BY_DESCRIPTION, FT_STATUS, LONG, LPDWORD,
|
FT_ResetDevice, FT_SetBitMode, FT_SetLatencyTimer, FT_Write, DWORD, FT_HANDLE,
|
||||||
LPVOID, PVOID, UCHAR,
|
FT_OPEN_BY_DESCRIPTION, FT_STATUS, LONG, LPDWORD, LPVOID, PVOID, UCHAR,
|
||||||
};
|
};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
pub mod mpsse;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BitMode {
|
pub enum BitMode {
|
||||||
Reset,
|
Reset,
|
||||||
@ -175,6 +177,7 @@ impl VCP {
|
|||||||
if result != Error::NoError {
|
if result != Error::NoError {
|
||||||
Err(result)
|
Err(result)
|
||||||
} else {
|
} else {
|
||||||
|
self.bit_mode = bitmode;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,27 +205,126 @@ impl VCP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, out_buffer: &[u8]) -> Result<usize, Error> {
|
pub fn latency_timer(&mut self) -> Result<u8, Error> {
|
||||||
let mut bytes_written = MaybeUninit::<DWORD>::uninit();
|
let mut latency = MaybeUninit::<UCHAR>::uninit();
|
||||||
let result = Error::from(unsafe {
|
let result = Error::from(unsafe { FT_GetLatencyTimer(self.handle, latency.as_mut_ptr()) });
|
||||||
FT_Write(
|
|
||||||
self.handle,
|
|
||||||
out_buffer.as_ptr() as LPVOID,
|
|
||||||
out_buffer
|
|
||||||
.len()
|
|
||||||
.try_into()
|
|
||||||
.expect("couldn't convert buffer length to DWORD"),
|
|
||||||
bytes_written.as_mut_ptr() as LPDWORD,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
if result != Error::NoError {
|
if result != Error::NoError {
|
||||||
Err(result)
|
Err(result)
|
||||||
} else {
|
} else {
|
||||||
Ok(unsafe { bytes_written.assume_init() }
|
Ok(unsafe { latency.assume_init() })
|
||||||
.try_into()
|
|
||||||
.expect("invalid number of bytes written"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_latency_timer(&mut self, latency: u8) -> Result<(), Error> {
|
||||||
|
let result = Error::from(unsafe { FT_SetLatencyTimer(self.handle, latency) });
|
||||||
|
if result != Error::NoError {
|
||||||
|
Err(result)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn write(&mut self, out_buffer: &[u8]) -> Result<usize, Error> {
|
||||||
|
// let mut bytes_written = MaybeUninit::<DWORD>::uninit();
|
||||||
|
// let result = Error::from(unsafe {
|
||||||
|
// FT_Write(
|
||||||
|
// self.handle,
|
||||||
|
// out_buffer.as_ptr() as LPVOID,
|
||||||
|
// out_buffer
|
||||||
|
// .len()
|
||||||
|
// .try_into()
|
||||||
|
// .expect("couldn't convert buffer length to DWORD"),
|
||||||
|
// bytes_written.as_mut_ptr() as LPDWORD,
|
||||||
|
// )
|
||||||
|
// });
|
||||||
|
// if result != Error::NoError {
|
||||||
|
// Err(result)
|
||||||
|
// } else {
|
||||||
|
// Ok(unsafe { bytes_written.assume_init() }
|
||||||
|
// .try_into()
|
||||||
|
// .expect("invalid number of bytes written"))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn set_gpio(&mut self, value: u8, direction: u8) -> Result<(), Error> {
|
||||||
|
match self.bit_mode {
|
||||||
|
BitMode::MPSSE => self
|
||||||
|
.write_all(&[mpsse::Command::MC_SETB_LOW.to_u8(), value, direction])
|
||||||
|
.or_else(|_| Err(Error::IoError)),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readb_low(&mut self) -> Result<u8, Error> {
|
||||||
|
self.write_all(&[mpsse::Command::MC_READB_LOW.to_u8()])
|
||||||
|
.or_else(|_| Err(Error::IoError))?;
|
||||||
|
let mut result = [0; 1];
|
||||||
|
self.read(&mut result).or_else(|_| Err(Error::IoError))?;
|
||||||
|
Ok(result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readb_high(&mut self) -> Result<u8, Error> {
|
||||||
|
self.write_all(&[mpsse::Command::MC_READB_HIGH.to_u8()])
|
||||||
|
.or_else(|_| Err(Error::IoError))?;
|
||||||
|
let mut result = [0; 1];
|
||||||
|
self.read(&mut result).or_else(|_| Err(Error::IoError))?;
|
||||||
|
Ok(result[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The purpose of this function is unclear. It appears to send some number
|
||||||
|
/// of bits out the line.
|
||||||
|
pub fn xfer_spi_bits(&mut self, data: u8, bits: usize) -> Result<u8, Error> {
|
||||||
|
if bits < 1 {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = &[
|
||||||
|
/* Input and output, update data on negative edge read on positive, bits. */
|
||||||
|
0x20 /*MC_DATA_IN*/ | 0x10 /*MC_DATA_OUT*/ | 0x01 /*MC_DATA_OCN*/ | 0x02, /*MC_DATA_BITS*/
|
||||||
|
bits as u8 - 1,
|
||||||
|
data,
|
||||||
|
];
|
||||||
|
self.write_all(buffer).or_else(|_| Err(Error::IoError))?;
|
||||||
|
|
||||||
|
let mut return_val = [0; 1];
|
||||||
|
self.read_exact(&mut return_val)
|
||||||
|
.or_else(|_| Err(Error::IoError))?;
|
||||||
|
Ok(return_val[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xfer_spi(&mut self, data: &mut [u8]) -> Result<(), Error> {
|
||||||
|
if data.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input and output, update data on negative edge read on positive. */
|
||||||
|
let buffer = &[
|
||||||
|
0x20 /*MC_DATA_IN*/ | 0x10 /*MC_DATA_OUT*/ | 0x01, /*MC_DATA_OCN*/
|
||||||
|
(data.len() - 1) as u8,
|
||||||
|
((data.len() - 1) / 256) as u8,
|
||||||
|
];
|
||||||
|
self.write_all(buffer).or_else(|_| Err(Error::IoError))?;
|
||||||
|
self.write_all(data).or_else(|_| Err(Error::IoError))?;
|
||||||
|
|
||||||
|
self.read_exact(data).or_else(|_| Err(Error::IoError))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_spi(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||||
|
if data.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input and output, update data on negative edge read on positive. */
|
||||||
|
let buffer = &[
|
||||||
|
0x10 /*MC_DATA_OUT*/ | 0x01, /*MC_DATA_OCN*/
|
||||||
|
(data.len() - 1) as u8,
|
||||||
|
((data.len() - 1) / 256) as u8,
|
||||||
|
];
|
||||||
|
self.write_all(buffer).or_else(|_| Err(Error::IoError))?;
|
||||||
|
self.write_all(data).or_else(|_| Err(Error::IoError))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for VCP {
|
impl Write for VCP {
|
||||||
@ -241,7 +343,13 @@ impl Write for VCP {
|
|||||||
if result != Error::NoError {
|
if result != Error::NoError {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
|
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
|
||||||
} else {
|
} else {
|
||||||
Ok(unsafe { bytes_written.assume_init() }
|
let bytes_written = unsafe { bytes_written.assume_init() };
|
||||||
|
// println!(
|
||||||
|
// "Wrote {} bytes (wanted to write {})",
|
||||||
|
// bytes_written,
|
||||||
|
// buf.len()
|
||||||
|
// );
|
||||||
|
Ok(bytes_written
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("invalid number of bytes written"))
|
.expect("invalid number of bytes written"))
|
||||||
}
|
}
|
||||||
@ -253,19 +361,23 @@ impl Write for VCP {
|
|||||||
|
|
||||||
impl Read for VCP {
|
impl Read for VCP {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
||||||
let mut bytes_written = MaybeUninit::<DWORD>::uninit();
|
let mut bytes_read = MaybeUninit::<DWORD>::uninit();
|
||||||
let result = Error::from(unsafe {
|
let result = Error::from(unsafe {
|
||||||
FT_Write(
|
FT_Read(
|
||||||
self.handle,
|
self.handle,
|
||||||
buf.as_ptr() as LPVOID,
|
buf.as_mut_ptr() as LPVOID,
|
||||||
1,
|
buf.len()
|
||||||
bytes_written.as_mut_ptr() as LPDWORD,
|
.try_into()
|
||||||
|
.expect("couldn't convert buffer length to DWORD"),
|
||||||
|
bytes_read.as_mut_ptr() as LPDWORD,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
if result != Error::NoError {
|
if result != Error::NoError {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
|
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
|
||||||
} else {
|
} else {
|
||||||
Ok(unsafe { bytes_written.assume_init() }
|
let bytes_read = unsafe { bytes_read.assume_init() };
|
||||||
|
// println!("Read {} bytes (wanted to read {})", bytes_read, buf.len());
|
||||||
|
Ok(bytes_read
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("invalid number of bytes written"))
|
.expect("invalid number of bytes written"))
|
||||||
}
|
}
|
||||||
|
141
ftdi-vcp-rs/src/mpsse.rs
Normal file
141
ftdi-vcp-rs/src/mpsse.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
// /* Transfer Command bits */
|
||||||
|
|
||||||
|
// /* All byte based commands consist of:
|
||||||
|
// * - Command byte
|
||||||
|
// * - Length lsb
|
||||||
|
// * - Length msb
|
||||||
|
// *
|
||||||
|
// * If data out is enabled the data follows after the above command bytes,
|
||||||
|
// * otherwise no additional data is needed.
|
||||||
|
// * - Data * n
|
||||||
|
// *
|
||||||
|
// * All bit based commands consist of:
|
||||||
|
// * - Command byte
|
||||||
|
// * - Length
|
||||||
|
// *
|
||||||
|
// * If data out is enabled a byte containing bitst to transfer follows.
|
||||||
|
// * Otherwise no additional data is needed. Only up to 8 bits can be transferred
|
||||||
|
// * per transaction when in bit mode.
|
||||||
|
// */
|
||||||
|
|
||||||
|
// /* b 0000 0000
|
||||||
|
// * |||| |||`- Data out negative enable. Update DO on negative clock edge.
|
||||||
|
// * |||| ||`-- Bit count enable. When reset count represents bytes.
|
||||||
|
// * |||| |`--- Data in negative enable. Latch DI on negative clock edge.
|
||||||
|
// * |||| `---- LSB enable. When set clock data out LSB first.
|
||||||
|
// * ||||
|
||||||
|
// * |||`------ Data out enable
|
||||||
|
// * ||`------- Data in enable
|
||||||
|
// * |`-------- TMS mode enable
|
||||||
|
// * `--------- Special command mode enable. See mpsse_cmd enum.
|
||||||
|
// */
|
||||||
|
// pub enum CommandBits {
|
||||||
|
|
||||||
|
// #define MC_DATA_TMS (0x40) /* When set use TMS mode */
|
||||||
|
// #define MC_DATA_IN (0x20) /* When set read data (Data IN) */
|
||||||
|
// #define MC_DATA_OUT (0x10) /* When set write data (Data OUT) */
|
||||||
|
// #define MC_DATA_LSB (0x08) /* When set input/output data LSB first. */
|
||||||
|
// #define MC_DATA_ICN (0x04) /* When set receive data on negative clock edge */
|
||||||
|
// #define MC_DATA_BITS (0x02) /* When set count bits not bytes */
|
||||||
|
// #define MC_DATA_OCN (0x01) /* When set update data on negative clock edge */
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* MPSSE engine command definitions */
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum Command {
|
||||||
|
// Mode commands
|
||||||
|
/// Set Data bits LowByte
|
||||||
|
MC_SETB_LOW,
|
||||||
|
|
||||||
|
/// Read Data bits LowByte
|
||||||
|
MC_READB_LOW,
|
||||||
|
/// Set Data bits HighByte
|
||||||
|
MC_SETB_HIGH,
|
||||||
|
/// Read data bits HighByte
|
||||||
|
MC_READB_HIGH,
|
||||||
|
/// Enable loopback
|
||||||
|
MC_LOOPBACK_EN,
|
||||||
|
/// Disable loopback
|
||||||
|
MC_LOOPBACK_DIS,
|
||||||
|
/// Set clock divisor
|
||||||
|
MC_SET_CLK_DIV,
|
||||||
|
/// Flush buffer fifos to the PC.
|
||||||
|
MC_FLUSH,
|
||||||
|
/// Wait on GPIOL1 to go high.
|
||||||
|
MC_WAIT_H,
|
||||||
|
/// Wait on GPIOL1 to go low.
|
||||||
|
MC_WAIT_L,
|
||||||
|
/// Disable /5 div, enables 60MHz master clock
|
||||||
|
MC_TCK_X5,
|
||||||
|
/// Enable /5 div, backward compat to FT2232D
|
||||||
|
MC_TCK_D5,
|
||||||
|
/// Enable 3 phase clk, DDR I2C
|
||||||
|
MC_EN_3PH_CLK,
|
||||||
|
/// Disable 3 phase clk
|
||||||
|
MC_DIS_3PH_CLK,
|
||||||
|
/// Clock every bit, used for JTAG
|
||||||
|
MC_CLK_N,
|
||||||
|
/// Clock every byte, used for JTAG
|
||||||
|
MC_CLK_N8,
|
||||||
|
/// Clock until GPIOL1 goes high
|
||||||
|
MC_CLK_TO_H,
|
||||||
|
/// Clock until GPIOL1 goes low
|
||||||
|
MC_CLK_TO_L,
|
||||||
|
/// Enable adaptive clocking
|
||||||
|
MC_EN_ADPT_CLK,
|
||||||
|
/// Disable adaptive clocking
|
||||||
|
MC_DIS_ADPT_CLK,
|
||||||
|
/// Clock until GPIOL1 goes high, count bytes
|
||||||
|
MC_CLK8_TO_H,
|
||||||
|
/// Clock until GPIOL1 goes low, count bytes
|
||||||
|
MC_CLK8_TO_L,
|
||||||
|
/// Set IO to only drive on 0 and tristate on 1
|
||||||
|
MC_TRI,
|
||||||
|
/// CPU mode commands
|
||||||
|
/// CPUMode read short address
|
||||||
|
MC_CPU_RS,
|
||||||
|
/// CPUMode read extended address
|
||||||
|
MC_CPU_RE,
|
||||||
|
/// CPUMode write short address
|
||||||
|
MC_CPU_WS,
|
||||||
|
/// CPUMode write extended address
|
||||||
|
MC_CPU_WE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn to_u8(&self) -> u8{
|
||||||
|
use Command::*;
|
||||||
|
match *self {
|
||||||
|
/* Mode commands */
|
||||||
|
MC_SETB_LOW => 0x80, /* Set Data bits LowByte */
|
||||||
|
MC_READB_LOW => 0x81, /* Read Data bits LowByte */
|
||||||
|
MC_SETB_HIGH => 0x82, /* Set Data bits HighByte */
|
||||||
|
MC_READB_HIGH => 0x83, /* Read data bits HighByte */
|
||||||
|
MC_LOOPBACK_EN => 0x84, /* Enable loopback */
|
||||||
|
MC_LOOPBACK_DIS => 0x85, /* Disable loopback */
|
||||||
|
MC_SET_CLK_DIV => 0x86, /* Set clock divisor */
|
||||||
|
MC_FLUSH => 0x87, /* Flush buffer fifos to the PC. */
|
||||||
|
MC_WAIT_H => 0x88, /* Wait on GPIOL1 to go high. */
|
||||||
|
MC_WAIT_L => 0x89, /* Wait on GPIOL1 to go low. */
|
||||||
|
MC_TCK_X5 => 0x8A, /* Disable /5 div, enables 60MHz master clock */
|
||||||
|
MC_TCK_D5 => 0x8B, /* Enable /5 div, backward compat to FT2232D */
|
||||||
|
MC_EN_3PH_CLK => 0x8C, /* Enable 3 phase clk, DDR I2C */
|
||||||
|
MC_DIS_3PH_CLK => 0x8D, /* Disable 3 phase clk */
|
||||||
|
MC_CLK_N => 0x8E, /* Clock every bit, used for JTAG */
|
||||||
|
MC_CLK_N8 => 0x8F, /* Clock every byte, used for JTAG */
|
||||||
|
MC_CLK_TO_H => 0x94, /* Clock until GPIOL1 goes high */
|
||||||
|
MC_CLK_TO_L => 0x95, /* Clock until GPIOL1 goes low */
|
||||||
|
MC_EN_ADPT_CLK => 0x96, /* Enable adaptive clocking */
|
||||||
|
MC_DIS_ADPT_CLK => 0x97, /* Disable adaptive clocking */
|
||||||
|
MC_CLK8_TO_H => 0x9C, /* Clock until GPIOL1 goes high, count bytes */
|
||||||
|
MC_CLK8_TO_L => 0x9D, /* Clock until GPIOL1 goes low, count bytes */
|
||||||
|
MC_TRI => 0x9E, /* Set IO to only drive on 0 and tristate on 1 */
|
||||||
|
/* CPU mode commands */
|
||||||
|
MC_CPU_RS => 0x90, /* CPUMode read short address */
|
||||||
|
MC_CPU_RE => 0x91, /* CPUMode read extended address */
|
||||||
|
MC_CPU_WS => 0x92, /* CPUMode write short address */
|
||||||
|
MC_CPU_WE => 0x93, /* CPUMode write extended address */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
364
src/flash.rs
Normal file
364
src/flash.rs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
use ftdi_vcp_rs::{mpsse::Command::*, BitMode, Error, VCP};
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub enum EraseType {
|
||||||
|
Kb64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Flash {
|
||||||
|
pub vcp: VCP,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Flash {
|
||||||
|
pub fn new(vcp: VCP) -> Flash {
|
||||||
|
Flash { vcp }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cs_creset(&mut self, cs_asserted: bool, reset_asserted: bool) -> Result<(), Error> {
|
||||||
|
let gpio = if cs_asserted { 0x10 } else { 0 } | if reset_asserted { 0x80 } else { 0 };
|
||||||
|
let direction = 0x93;
|
||||||
|
self.vcp.set_gpio(gpio, direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the FPGA reset is released so also FLASH chip select should be deasserted
|
||||||
|
pub fn release_reset(&mut self) -> Result<(), Error> {
|
||||||
|
self.set_cs_creset(true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLASH chip select assert
|
||||||
|
// should only happen while FPGA reset is asserted
|
||||||
|
pub fn chip_select(&mut self) -> Result<(), Error> {
|
||||||
|
self.set_cs_creset(false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLASH chip select deassert
|
||||||
|
pub fn chip_deselect(&mut self) -> Result<(), Error> {
|
||||||
|
self.set_cs_creset(true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) -> Result<(), ftdi_vcp_rs::Error> {
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi_bits(0xFF, 8)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi_bits(0xFF, 2)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_up(&mut self) -> Result<(), Error> {
|
||||||
|
let mut cmd = [0xAB /* FC_RPD */];
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut cmd)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_down(&mut self) -> Result<(), Error> {
|
||||||
|
let mut cmd = [0xB9 /* FC_PD */];
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut cmd)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_id(&mut self, verbose: bool) -> Result<(), Error> {
|
||||||
|
/* JEDEC ID structure:
|
||||||
|
* Byte No. | Data Type
|
||||||
|
* ---------+----------
|
||||||
|
* 0 | FC_JEDECID Request Command
|
||||||
|
* 1 | MFG ID
|
||||||
|
* 2 | Dev ID 1
|
||||||
|
* 3 | Dev ID 2
|
||||||
|
* 4 | Ext Dev Str Len
|
||||||
|
*/
|
||||||
|
let mut data = [0xffu8; 260];
|
||||||
|
data[0] = 0x9Fu8 /* FC_JEDECID - Read JEDEC ID */;
|
||||||
|
let mut len = 5usize; // command + 4 response bytes
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("read flash ID..");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
|
||||||
|
// Write command and read first 4 bytes
|
||||||
|
self.vcp.xfer_spi(&mut data[0..=4])?;
|
||||||
|
|
||||||
|
if data[4] == 0xFF {
|
||||||
|
println!(
|
||||||
|
"Extended Device String Length is 0xFF, this is likely a read error. Ignorig..."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Read extended JEDEC ID bytes
|
||||||
|
if data[4] != 0 {
|
||||||
|
len += data[4] as usize;
|
||||||
|
let (_, mut jedec_data) = data.split_at_mut(4);
|
||||||
|
self.vcp.xfer_spi(&mut jedec_data)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
// TODO: Add full decode of the JEDEC ID.
|
||||||
|
print!("flash ID:");
|
||||||
|
for b in &data[1..len] {
|
||||||
|
print!(" 0x{:02X}", b);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cdone(&mut self) -> Result<bool, Error> {
|
||||||
|
// ADBUS6 (GPIOL2)
|
||||||
|
match self.vcp.readb_low()? & 0x40 {
|
||||||
|
0 => Ok(false),
|
||||||
|
_ => Ok(true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cdone_str(&mut self) -> Result<&'static str, Error> {
|
||||||
|
if self.cdone()? {
|
||||||
|
Ok("high")
|
||||||
|
} else {
|
||||||
|
Ok("low")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_enable(&mut self, verbose: bool) -> Result<(), Error> {
|
||||||
|
if verbose {
|
||||||
|
println!("status before enable:");
|
||||||
|
self.read_status(verbose)?;
|
||||||
|
println!("write enable..");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = [0x06 /* FC_WE // Write Enable */];
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("status after enable:");
|
||||||
|
self.read_status(verbose)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bulk_erase(&mut self) -> Result<(), Error> {
|
||||||
|
println!("bulk erase..");
|
||||||
|
let mut data = [0xC7 /* FC_CE // Chip Erase */];
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sector_erase(&mut self, erase_type: EraseType, addr: usize) -> Result<(), Error> {
|
||||||
|
let erase_cmd = match erase_type {
|
||||||
|
EraseType::Kb64 => 0xD8, /* FC_BE64 // Block Erase 64kb */
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("erase 64kB sector at 0x{:06X}..", addr);
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp
|
||||||
|
.send_spi(&[erase_cmd, (addr >> 16) as u8, (addr >> 8) as u8, addr as u8])?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_status(&mut self, verbose: bool) -> Result<u8, Error> {
|
||||||
|
let mut data = [0x05 /* FC_RSR1 // Read Status Register 1 */, 0x00];
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!("SR1: 0x{:02X}", data[1]);
|
||||||
|
println!(
|
||||||
|
" - SPRL: {}",
|
||||||
|
if data[1] & (1 << 7) == 0 {
|
||||||
|
"unlocked"
|
||||||
|
} else {
|
||||||
|
"locked"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" - SPM: {}",
|
||||||
|
if data[1] & (1 << 6) == 0 {
|
||||||
|
"Byte/Page Prog Mode"
|
||||||
|
} else {
|
||||||
|
"Sequential Prog Mode"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" - EPE: {}\n",
|
||||||
|
if data[1] & (1 << 5) == 0 {
|
||||||
|
"Erase/Prog success"
|
||||||
|
} else {
|
||||||
|
"Erase/Prog error"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"- SPM: {}\n",
|
||||||
|
if data[1] & (1 << 4) == 0 {
|
||||||
|
"~WP asserted"
|
||||||
|
} else {
|
||||||
|
"~WP deasserted"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" - SWP: {}",
|
||||||
|
match (data[1] >> 2) & 0x3 {
|
||||||
|
0 => "All sectors unprotected",
|
||||||
|
1 => "Some sectors protected",
|
||||||
|
2 => "Reserved (xxxx 10xx)",
|
||||||
|
3 => "All sectors protected",
|
||||||
|
_ => panic!("math is broken!"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" - WEL: {}",
|
||||||
|
if (data[1] & (1 << 1)) == 0 {
|
||||||
|
"Not write enabled"
|
||||||
|
} else {
|
||||||
|
"Write enabled"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" - ~RDY: {}",
|
||||||
|
if (data[1] & (1 << 0)) == 0 {
|
||||||
|
"Ready"
|
||||||
|
} else {
|
||||||
|
"Busy"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_micros(1_000));
|
||||||
|
|
||||||
|
Ok(data[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_protection(&mut self) -> Result<(), Error> {
|
||||||
|
println!("disable flash protection...");
|
||||||
|
|
||||||
|
// Write Status Register 1 <- 0x00
|
||||||
|
let mut data = [0x01 /* FC_WSR1 // Write Status Register 1 */, 0x00];
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
self.wait(false)?;
|
||||||
|
|
||||||
|
// Read Status Register 1
|
||||||
|
data[0] = 0x05; // FC_RSR1;
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
if data[1] != 0x00 {
|
||||||
|
println!(
|
||||||
|
"failed to disable protection, SR now equal to 0x{:02x} (expected 0x00)\n",
|
||||||
|
data[1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait(&mut self, verbose: bool) -> Result<(), Error> {
|
||||||
|
if verbose {
|
||||||
|
println!("waiting..");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
loop {
|
||||||
|
let mut data = [0x05 /* FC_RSR1 // Read Status Register 1 */, 0x00];
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.xfer_spi(&mut data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
if (data[1] & 0x01) == 0 {
|
||||||
|
if count < 2 {
|
||||||
|
count += 1;
|
||||||
|
if verbose {
|
||||||
|
print!("r");
|
||||||
|
//fflush(stderr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if verbose {
|
||||||
|
print!("R");
|
||||||
|
// fflush(stderr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if verbose {
|
||||||
|
print!(".");
|
||||||
|
// fflush(stderr);
|
||||||
|
}
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_micros(1_000));
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prog(&mut self, addr: usize, data: &[u8], verbose: bool) -> Result<(), Error> {
|
||||||
|
if verbose {
|
||||||
|
println!("prog 0x{:06X} +0x{:03X}..", addr, data.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = [
|
||||||
|
0x02, /* FC_PP // Page Program */
|
||||||
|
(addr >> 16) as u8,
|
||||||
|
(addr >> 8) as u8,
|
||||||
|
addr as u8,
|
||||||
|
];
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.send_spi(&command)?;
|
||||||
|
self.vcp.send_spi(data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
// if verbose {
|
||||||
|
// for (int i = 0; i < n; i++)
|
||||||
|
// fprintf(stderr, "%02x%c", data[i], i == n - 1 || i % 32 == 31 ? '\n' : ' ');
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, addr: usize, data: &mut [u8], verbose: bool) -> Result<(), Error> {
|
||||||
|
if verbose {
|
||||||
|
println!("read 0x{:06X} +0x{:03X}..", addr, data.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = [
|
||||||
|
0x03, /*FC_RD // Read Data */
|
||||||
|
(addr >> 16) as u8,
|
||||||
|
(addr >> 8) as u8,
|
||||||
|
addr as u8,
|
||||||
|
];
|
||||||
|
|
||||||
|
self.chip_select()?;
|
||||||
|
self.vcp.send_spi(&command)?;
|
||||||
|
self.vcp.xfer_spi(data)?;
|
||||||
|
self.chip_deselect()?;
|
||||||
|
|
||||||
|
// if (verbose)
|
||||||
|
// for (int i = 0; i < n; i++)
|
||||||
|
// fprintf(stderr, "%02x%c", data[i], i == n - 1 || i % 32 == 31 ? '\n' : ' ');
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
209
src/main.rs
209
src/main.rs
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
#define FT_LIST_NUMBER_ONLY 0x80000000
|
#define FT_LIST_NUMBER_ONLY 0x80000000
|
||||||
#define FT_LIST_BY_INDEX 0x40000000
|
#define FT_LIST_BY_INDEX 0x40000000
|
||||||
@ -6,24 +5,216 @@
|
|||||||
|
|
||||||
#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL)
|
#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL)
|
||||||
*/
|
*/
|
||||||
use ftdi_vcp_rs::{VCP, BitMode};
|
use clap::{App, Arg, SubCommand};
|
||||||
|
use ftdi_vcp_rs::{mpsse::Command::*, BitMode, VCP};
|
||||||
|
use std::io::{Write, Read};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
mod flash;
|
||||||
|
|
||||||
fn main() -> Result<(), ftdi_vcp_rs::Error> {
|
fn main() -> Result<(), ftdi_vcp_rs::Error> {
|
||||||
let mut vcp = VCP::new_from_name("iCEBreaker V1.0e A").expect("couldn't open vcp");
|
let mut vcp = VCP::new_from_name("iCEBreaker V1.0e A").expect("couldn't open vcp");
|
||||||
|
let slow_clock = false;
|
||||||
|
let read_mode = false;
|
||||||
|
let check_mode = false;
|
||||||
|
let dont_erase = false;
|
||||||
|
let bulk_erase = false;
|
||||||
|
let disable_verify = false;
|
||||||
|
let erase_mode = false;
|
||||||
|
let rw_offset = 0;
|
||||||
|
let disable_protect = false;
|
||||||
|
let read_size = 256 * 1024;
|
||||||
|
|
||||||
|
let mut bitstream = vec![];
|
||||||
|
|
||||||
|
let matches = App::new("iCE40 Programmer")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Sean Cross <sean@xobs.io>")
|
||||||
|
.about("Port of Iceprog")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("FILENAME")
|
||||||
|
.help("Sets the bitstream file to read or write")
|
||||||
|
.required(true)
|
||||||
|
.index(1),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("v")
|
||||||
|
.short("v")
|
||||||
|
.multiple(true)
|
||||||
|
.help("Sets the level of verbosity"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("test")
|
||||||
|
.short("t")
|
||||||
|
.help("Run tests"),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let verbose = matches.is_present("verbose");
|
||||||
|
let test_mode = matches.is_present("test");
|
||||||
|
|
||||||
|
let bitstream = {
|
||||||
|
let filename = matches.value_of("FILENAME").unwrap();
|
||||||
|
let mut file = File::open(filename).expect("Couldn't open path");
|
||||||
|
file.read_to_end(&mut bitstream).expect("couldn't read to end");
|
||||||
|
bitstream
|
||||||
|
};
|
||||||
|
|
||||||
println!("Opened VCP: {:?}", vcp);
|
println!("Opened VCP: {:?}", vcp);
|
||||||
vcp.reset()?;
|
vcp.reset()?;
|
||||||
vcp.set_bitmode(0x80, BitMode::SyncBitbang)?;
|
vcp.purge()?;
|
||||||
|
|
||||||
for i in 0..10 {
|
let previous_latency = vcp.latency_timer()?;
|
||||||
if i & 1 != 0 {
|
vcp.set_latency_timer(1)?;
|
||||||
vcp.write(&[0x80])?;
|
|
||||||
|
vcp.set_bitmode(0xff, BitMode::MPSSE)?;
|
||||||
|
|
||||||
|
// enable clock divide by 5
|
||||||
|
vcp.write(&[MC_TCK_D5.to_u8()])
|
||||||
|
.or_else(|_| Err(ftdi_vcp_rs::Error::IoError))?;
|
||||||
|
|
||||||
|
if slow_clock {
|
||||||
|
// set 50 kHz clock
|
||||||
|
vcp.write(&[MC_SET_CLK_DIV.to_u8(), 119, 0x00])
|
||||||
|
.or_else(|_| Err(ftdi_vcp_rs::Error::IoError))?;
|
||||||
} else {
|
} else {
|
||||||
vcp.write(&[0x00])?;
|
// set 6 MHz clock
|
||||||
|
vcp.write(&[MC_SET_CLK_DIV.to_u8(), 0x00, 0x00])
|
||||||
|
.or_else(|_| Err(ftdi_vcp_rs::Error::IoError))?;
|
||||||
}
|
}
|
||||||
sleep(Duration::from_millis(500));
|
|
||||||
|
let mut flash = flash::Flash::new(vcp);
|
||||||
|
flash.release_reset()?;
|
||||||
|
|
||||||
|
sleep(Duration::from_micros(100_000));
|
||||||
|
|
||||||
|
if test_mode {
|
||||||
|
println!("reset..");
|
||||||
|
|
||||||
|
flash.chip_deselect()?;
|
||||||
|
sleep(Duration::from_micros(250_000));
|
||||||
|
|
||||||
|
println!("cdone: {}", flash.cdone_str()?);
|
||||||
|
|
||||||
|
flash.reset()?;
|
||||||
|
flash.power_up()?;
|
||||||
|
|
||||||
|
flash.read_id(true)?;
|
||||||
|
|
||||||
|
flash.power_down()?;
|
||||||
|
|
||||||
|
flash.release_reset()?;
|
||||||
|
sleep(Duration::from_micros(250_000));
|
||||||
|
|
||||||
|
println!("cdone: {}", flash.cdone_str()?);
|
||||||
|
} else {
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Reset
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
println!("reset..");
|
||||||
|
|
||||||
|
flash.chip_deselect()?;
|
||||||
|
sleep(Duration::from_micros(250_000));
|
||||||
|
|
||||||
|
println!("cdone: {}", flash.cdone_str()?);
|
||||||
|
|
||||||
|
flash.reset()?;
|
||||||
|
flash.power_up()?;
|
||||||
|
|
||||||
|
flash.read_id(true)?;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Program
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
if !read_mode && !check_mode {
|
||||||
|
if disable_protect {
|
||||||
|
flash.write_enable(verbose)?;
|
||||||
|
flash.disable_protection()?;
|
||||||
}
|
}
|
||||||
println!("VCP COM{}:", vcp.com_port()?);
|
|
||||||
|
if !dont_erase {
|
||||||
|
if bulk_erase {
|
||||||
|
flash.write_enable(verbose)?;
|
||||||
|
flash.bulk_erase()?;
|
||||||
|
flash.wait(verbose)?;
|
||||||
|
} else {
|
||||||
|
println!("file size: {}", bitstream.len());
|
||||||
|
|
||||||
|
let begin_addr = rw_offset & !0xffff;
|
||||||
|
let end_addr = (rw_offset + bitstream.len() + 0xffff) & !0xffff;
|
||||||
|
|
||||||
|
for addr in (begin_addr..end_addr).step_by(0x10000) {
|
||||||
|
flash.write_enable(verbose)?;
|
||||||
|
flash.sector_erase(flash::EraseType::Kb64, addr)?;
|
||||||
|
if verbose {
|
||||||
|
println!("Status after block erase:");
|
||||||
|
flash.read_status(verbose)?;
|
||||||
|
}
|
||||||
|
flash.wait(verbose)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !erase_mode {
|
||||||
|
println!("programming..");
|
||||||
|
|
||||||
|
for (idx, page) in bitstream.chunks(256).enumerate() {
|
||||||
|
flash.write_enable(verbose)?;
|
||||||
|
flash.prog(rw_offset + idx*256, page, verbose)?;
|
||||||
|
flash.wait(verbose)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* seek to the beginning for second pass */
|
||||||
|
// fseek(f, 0, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Read/Verify
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
if read_mode {
|
||||||
|
println!("reading..");
|
||||||
|
for addr in (0..read_size).step_by(256) {
|
||||||
|
// uint8_t buffer[256];
|
||||||
|
// flash_read(rw_offset + addr, buffer, 256);
|
||||||
|
// fwrite(buffer, read_size - addr > 256 ? 256 : read_size - addr, 1, f);
|
||||||
|
}
|
||||||
|
} else if !erase_mode && !disable_verify {
|
||||||
|
println!("reading..");
|
||||||
|
for (idx, page) in bitstream.chunks(256).enumerate() {
|
||||||
|
let mut buffer_flash = [0; 256];
|
||||||
|
flash.read(rw_offset + idx*256, &mut buffer_flash, verbose);
|
||||||
|
if ! page.iter().zip(buffer_flash.iter()).all(|(a,b)| a == b) {
|
||||||
|
println!("Found difference between flash and file!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("VERIFY OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Reset
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
flash.power_down()?;
|
||||||
|
|
||||||
|
flash.release_reset();
|
||||||
|
sleep(Duration::from_micros(250_000));
|
||||||
|
|
||||||
|
println!("cdone: {}", flash.cdone_str()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(com_port) = flash.vcp.com_port() {
|
||||||
|
println!("VCP COM{}:", com_port);
|
||||||
|
} else {
|
||||||
|
println!("No COM port assigned");
|
||||||
|
}
|
||||||
|
|
||||||
|
flash.vcp.set_latency_timer(previous_latency)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
0
src/mpsse.rs
Normal file
0
src/mpsse.rs
Normal file
Loading…
Reference in New Issue
Block a user