Compare commits

..

4 Commits

Author SHA1 Message Date
occheung e994000df1 fpga config: included into lib 2020-09-17 11:21:24 +08:00
occheung 89f9b48073 fpga config: fix spi transmission 2020-09-17 10:21:50 +08:00
occheung 4d73786880 migen: fix comment 2020-09-17 10:06:33 +08:00
occheung 52b285742e migen: use extension 2020-09-17 09:55:20 +08:00
4 changed files with 135 additions and 131 deletions

View File

@ -1,8 +1,9 @@
#![no_main]
#![no_std]
#[macro_use]
extern crate log;
use log::debug;
use stm32h7xx_hal::hal::digital::v2::{
InputPin,
OutputPin,
@ -16,6 +17,8 @@ use cortex_m_rt::entry;
use core::ptr;
use nb::block;
use firmware::flash::flash_ice40_fpga;
#[path = "util/logger.rs"]
mod logger;
@ -73,71 +76,10 @@ fn main() -> ! {
&ccdr.clocks,
);
// Data buffer setup
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();
// Pre-load the configuration bytes
let config_data = include_bytes!("../build/top.bin");
fpga_cfg_spi.transfer(&config_data).unwrap();
// Send the whole image without interruption
// 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.");
}
flash_ice40_fpga(fpga_cfg_spi, fpga_ss, fpga_creset, fpga_cdone, delay, config_data)?;
loop {
nop();

View File

@ -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.specials import Instance
from migen.fhdl.bitcontainer import value_bits_sign
from migen.genlib.io import *
from migen.build.lattice.common import LatticeiCE40DifferentialInputImpl
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):
def __init__(self, platform):
@ -13,6 +23,8 @@ class UrukulConnector(Module):
eem0 = [
platform.request("eem0", 0),
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", 3),
platform.request("eem0", 4),
@ -20,19 +32,17 @@ class UrukulConnector(Module):
platform.request("eem0", 6)
]
spi = platform.request("spi")
spi_cs = platform.request("spi_cs")
led = platform.request("user_led")
io_update = platform.request("io_update")
# Assert SPI resource length
assert len(spi.sclk) == 1
assert len(spi.clk) == 1
assert len(spi.mosi) == 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
assert isinstance(eem0, list)
# Flip positive signal as negative output, maybe only do it for FPGA outputs
# Flip negative input to positive output
self.miso_n = Signal()
# 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
self.comb += [
eem0[0].p.eq(spi.sclk),
eem0[0].n.eq(~spi.sclk),
eem0[0].p.eq(spi.clk),
eem0[0].n.eq(~spi.clk),
eem0[1].p.eq(spi.mosi),
eem0[1].n.eq(~spi.mosi),
spi.miso.eq(~self.miso_n),
eem0[3].p.eq(spi.cs[0]),
eem0[3].n.eq(~spi.cs[0]),
eem0[3].p.eq(spi_cs[0]),
eem0[3].n.eq(~spi_cs[0]),
eem0[4].p.eq(spi.cs[1]),
eem0[4].n.eq(~spi.cs[1]),
eem0[4].p.eq(spi_cs[1]),
eem0[4].n.eq(~spi_cs[1]),
eem0[5].p.eq(spi.cs[2]),
eem0[5].n.eq(~spi.cs[2]),
eem0[5].p.eq(spi_cs[2]),
eem0[5].n.eq(~spi_cs[2]),
eem0[6].p.eq(io_update),
eem0[6].n.eq(~io_update),
@ -71,5 +81,7 @@ class UrukulConnector(Module):
if __name__ == "__main__":
platform = HumpbackPlatform()
platform = humpback.Platform()
platform.add_extension(spi_cs)
platform.add_extension(io_update)
platform.build(UrukulConnector(platform))

View File

@ -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()

99
src/flash.rs Normal file
View File

@ -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(())
}