Compare commits
4 Commits
1be44e256d
...
e994000df1
Author | SHA1 | Date |
---|---|---|
occheung | e994000df1 | |
occheung | 89f9b48073 | |
occheung | 4d73786880 | |
occheung | 52b285742e |
|
@ -1,8 +1,9 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
use stm32h7xx_hal::hal::digital::v2::{
|
use stm32h7xx_hal::hal::digital::v2::{
|
||||||
InputPin,
|
InputPin,
|
||||||
OutputPin,
|
OutputPin,
|
||||||
|
@ -16,6 +17,8 @@ use cortex_m_rt::entry;
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use nb::block;
|
use nb::block;
|
||||||
|
|
||||||
|
use firmware::flash::flash_ice40_fpga;
|
||||||
|
|
||||||
#[path = "util/logger.rs"]
|
#[path = "util/logger.rs"]
|
||||||
mod logger;
|
mod logger;
|
||||||
|
|
||||||
|
@ -73,71 +76,10 @@ fn main() -> ! {
|
||||||
&ccdr.clocks,
|
&ccdr.clocks,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Data buffer setup
|
// Pre-load the configuration bytes
|
||||||
let mut dummy_byte :[u8; 1] = [0x00];
|
|
||||||
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
|
|
||||||
|
|
||||||
// Drive CRESET_B low
|
|
||||||
fpga_creset.set_low().unwrap();
|
|
||||||
|
|
||||||
// Drive SPI_SS_B low
|
|
||||||
fpga_ss.set_low().unwrap();
|
|
||||||
|
|
||||||
// Wait at least 200ns
|
|
||||||
delay.delay_us(1_u16);
|
|
||||||
|
|
||||||
// Drive CRESET_B high
|
|
||||||
fpga_creset.set_high().unwrap();
|
|
||||||
|
|
||||||
// Wait at least another 1200us to clear internal config memory
|
|
||||||
delay.delay_us(1200_u16);
|
|
||||||
|
|
||||||
// Before data transmission starts, check if C_DONE is truly dine
|
|
||||||
match fpga_cdone.is_high() {
|
|
||||||
Ok(false) => debug!("Reset successful!"),
|
|
||||||
Ok(_) => debug!("Reset unsuccessful!"),
|
|
||||||
Err(_) => debug!("Reset error!"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set SPI_SS_B high
|
|
||||||
fpga_ss.set_high().unwrap();
|
|
||||||
|
|
||||||
// Send 8 dummy clock, effectively 1 byte of 0x00
|
|
||||||
fpga_cfg_spi.transfer(&mut dummy_byte).unwrap();
|
|
||||||
|
|
||||||
// Drive SPI_SS_B low
|
|
||||||
fpga_ss.set_low().unwrap();
|
|
||||||
|
|
||||||
let config_data = include_bytes!("../build/top.bin");
|
let config_data = include_bytes!("../build/top.bin");
|
||||||
fpga_cfg_spi.transfer(&config_data).unwrap();
|
|
||||||
|
|
||||||
// Send the whole image without interruption
|
flash_ice40_fpga(fpga_cfg_spi, fpga_ss, fpga_creset, fpga_cdone, delay, config_data)?;
|
||||||
// let base_address = 0x08100000;
|
|
||||||
// let size = 135100;
|
|
||||||
// for index in 0..size {
|
|
||||||
// unsafe {
|
|
||||||
// let data :u8 = ptr::read_volatile((base_address + index) as *const u8);
|
|
||||||
// block!(fpga_cfg_spi.send(data)).unwrap();
|
|
||||||
// block!(fpga_cfg_spi.read()).unwrap();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Drive SPI_SS_B high
|
|
||||||
fpga_ss.set_high().unwrap();
|
|
||||||
|
|
||||||
// Send at another 100 dummy clocks (choosing 13 bytes)
|
|
||||||
fpga_cfg_spi.transfer(&mut dummy_13_bytes).unwrap();
|
|
||||||
|
|
||||||
// Check the CDONE output from FPGA
|
|
||||||
if !(fpga_cdone.is_high().unwrap()) {
|
|
||||||
debug!("ERROR!");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug!("Configuration successful!");
|
|
||||||
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
|
|
||||||
fpga_cfg_spi.transfer(&mut dummy_13_bytes).unwrap();
|
|
||||||
debug!("User I/O pins activated.");
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
nop();
|
nop();
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
from humpback import HumpbackPlatform
|
# Import built in I/O, Connectors & Platform template for Humpback
|
||||||
|
from migen.build.platforms.sinara import humpback
|
||||||
|
# Import migen platform for Lattice Products
|
||||||
|
from migen.build.lattice import LatticePlatform
|
||||||
|
# Import migen pin record structure
|
||||||
|
from migen.build.generic_platform import *
|
||||||
from migen.fhdl.module import Module
|
from migen.fhdl.module import Module
|
||||||
from migen.fhdl.specials import Instance
|
from migen.fhdl.specials import Instance
|
||||||
from migen.fhdl.bitcontainer import value_bits_sign
|
|
||||||
from migen.genlib.io import *
|
from migen.genlib.io import *
|
||||||
from migen.build.lattice.common import LatticeiCE40DifferentialInputImpl
|
from migen.build.lattice.common import LatticeiCE40DifferentialInputImpl
|
||||||
from migen.genlib.io import DifferentialInput
|
from migen.genlib.io import DifferentialInput
|
||||||
|
|
||||||
|
spi_cs = [
|
||||||
|
("spi_cs", 0, Pins("B13 B14 B15"), IOStandard("LVCMOS33"))
|
||||||
|
]
|
||||||
|
io_update = [
|
||||||
|
("io_update", 0, Pins("A11"), IOStandard("LVCMOS33"))
|
||||||
|
]
|
||||||
|
|
||||||
class UrukulConnector(Module):
|
class UrukulConnector(Module):
|
||||||
def __init__(self, platform):
|
def __init__(self, platform):
|
||||||
|
@ -13,6 +23,8 @@ class UrukulConnector(Module):
|
||||||
eem0 = [
|
eem0 = [
|
||||||
platform.request("eem0", 0),
|
platform.request("eem0", 0),
|
||||||
platform.request("eem0", 1),
|
platform.request("eem0", 1),
|
||||||
|
# Supply EEM pin with negative polarity
|
||||||
|
# See issue/PR: https://github.com/m-labs/migen/pull/181
|
||||||
platform.request("eem0_n", 2),
|
platform.request("eem0_n", 2),
|
||||||
platform.request("eem0", 3),
|
platform.request("eem0", 3),
|
||||||
platform.request("eem0", 4),
|
platform.request("eem0", 4),
|
||||||
|
@ -20,19 +32,17 @@ class UrukulConnector(Module):
|
||||||
platform.request("eem0", 6)
|
platform.request("eem0", 6)
|
||||||
]
|
]
|
||||||
spi = platform.request("spi")
|
spi = platform.request("spi")
|
||||||
|
spi_cs = platform.request("spi_cs")
|
||||||
led = platform.request("user_led")
|
led = platform.request("user_led")
|
||||||
io_update = platform.request("io_update")
|
io_update = platform.request("io_update")
|
||||||
|
|
||||||
# Assert SPI resource length
|
assert len(spi.clk) == 1
|
||||||
assert len(spi.sclk) == 1
|
|
||||||
assert len(spi.mosi) == 1
|
assert len(spi.mosi) == 1
|
||||||
assert len(spi.miso) == 1
|
assert len(spi.miso) == 1
|
||||||
assert len(spi.cs) == 3
|
assert len(spi_cs) == 3
|
||||||
|
assert len(io_update) == 1
|
||||||
|
|
||||||
# TODO: Assert EEM resources
|
# Flip negative input to positive output
|
||||||
assert isinstance(eem0, list)
|
|
||||||
|
|
||||||
# Flip positive signal as negative output, maybe only do it for FPGA outputs
|
|
||||||
self.miso_n = Signal()
|
self.miso_n = Signal()
|
||||||
|
|
||||||
# Very similar setup to Diff setup for iCE40 suggested, but gave B pin instead
|
# Very similar setup to Diff setup for iCE40 suggested, but gave B pin instead
|
||||||
|
@ -46,22 +56,22 @@ class UrukulConnector(Module):
|
||||||
# Link EEM to SPI
|
# Link EEM to SPI
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
|
||||||
eem0[0].p.eq(spi.sclk),
|
eem0[0].p.eq(spi.clk),
|
||||||
eem0[0].n.eq(~spi.sclk),
|
eem0[0].n.eq(~spi.clk),
|
||||||
|
|
||||||
eem0[1].p.eq(spi.mosi),
|
eem0[1].p.eq(spi.mosi),
|
||||||
eem0[1].n.eq(~spi.mosi),
|
eem0[1].n.eq(~spi.mosi),
|
||||||
|
|
||||||
spi.miso.eq(~self.miso_n),
|
spi.miso.eq(~self.miso_n),
|
||||||
|
|
||||||
eem0[3].p.eq(spi.cs[0]),
|
eem0[3].p.eq(spi_cs[0]),
|
||||||
eem0[3].n.eq(~spi.cs[0]),
|
eem0[3].n.eq(~spi_cs[0]),
|
||||||
|
|
||||||
eem0[4].p.eq(spi.cs[1]),
|
eem0[4].p.eq(spi_cs[1]),
|
||||||
eem0[4].n.eq(~spi.cs[1]),
|
eem0[4].n.eq(~spi_cs[1]),
|
||||||
|
|
||||||
eem0[5].p.eq(spi.cs[2]),
|
eem0[5].p.eq(spi_cs[2]),
|
||||||
eem0[5].n.eq(~spi.cs[2]),
|
eem0[5].n.eq(~spi_cs[2]),
|
||||||
|
|
||||||
eem0[6].p.eq(io_update),
|
eem0[6].p.eq(io_update),
|
||||||
eem0[6].n.eq(~io_update),
|
eem0[6].n.eq(~io_update),
|
||||||
|
@ -71,5 +81,7 @@ class UrukulConnector(Module):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
platform = HumpbackPlatform()
|
platform = humpback.Platform()
|
||||||
|
platform.add_extension(spi_cs)
|
||||||
|
platform.add_extension(io_update)
|
||||||
platform.build(UrukulConnector(platform))
|
platform.build(UrukulConnector(platform))
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
# Import built in I/O, Connectors & Platform template
|
|
||||||
from migen.build.platforms.sinara.humpback import _io, _connectors, Platform
|
|
||||||
|
|
||||||
# Import migen platform for Lattice Products
|
|
||||||
from migen.build.lattice import LatticePlatform
|
|
||||||
|
|
||||||
# Import migen pin record structure
|
|
||||||
from migen.build.generic_platform import *
|
|
||||||
|
|
||||||
# Modify the SPI record, to include all 3 CS pins
|
|
||||||
'''
|
|
||||||
sclk -> PA5 : C8
|
|
||||||
mosi -> PB5 : N5
|
|
||||||
miso -> PA6 : T2
|
|
||||||
cs_0 -> PB12: B13
|
|
||||||
cs_1 -> PA15: B14
|
|
||||||
cs_2 -> PC7 : B15
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Filter out SPI record
|
|
||||||
_io = [record for record in _io if record[0] != "spi"]
|
|
||||||
|
|
||||||
# Reinsert new SPI record, without MISO
|
|
||||||
_io.append(
|
|
||||||
("spi", 0,
|
|
||||||
Subsignal("cs" , Pins("B13 B14 B15")),
|
|
||||||
Subsignal("sclk", Pins("C8")),
|
|
||||||
Subsignal("mosi", Pins("N5")),
|
|
||||||
Subsignal("miso", Pins("T2")),
|
|
||||||
IOStandard("LVCMOS33"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Resource: DDS I/O_Update
|
|
||||||
'''
|
|
||||||
io_update -> PB15 : A11
|
|
||||||
'''
|
|
||||||
_io.append(
|
|
||||||
("io_update", 0, Pins("A11"), IOStandard("LVCMOS33"))
|
|
||||||
)
|
|
||||||
|
|
||||||
# Inherit Platform to gain the programmed clock attribute
|
|
||||||
class HumpbackPlatform(Platform):
|
|
||||||
def __init__(self):
|
|
||||||
LatticePlatform.__init__(self, "ice40-hx8k-ct256", _io, _connectors, toolchain="icestorm")
|
|
||||||
|
|
||||||
# Syntax check for direct execution
|
|
||||||
if __name__ == "__main__":
|
|
||||||
platform = HumpbackPlatform()
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
use embedded_hal::{
|
||||||
|
digital::v2::{OutputPin, InputPin},
|
||||||
|
blocking::spi::Transfer,
|
||||||
|
blocking::delay::{DelayMs, DelayUs},
|
||||||
|
};
|
||||||
|
|
||||||
|
use cortex_m;
|
||||||
|
use cortex_m::asm::nop;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
|
||||||
|
use core::ptr;
|
||||||
|
use nb::block;
|
||||||
|
|
||||||
|
use log::{warn, debug};
|
||||||
|
|
||||||
|
pub enum FPGAFlashError {
|
||||||
|
SPICommunicationError,
|
||||||
|
NegotiationError,
|
||||||
|
ResetStatusError,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, data: &[u8]) -> 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),
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Configuration successful!");
|
||||||
|
// 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)?;
|
||||||
|
debug!("User I/O pins activated.");
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue