add flashing FPGA via SPI

This commit is contained in:
Zheng-Jiakun 2021-08-31 17:40:52 +08:00
parent 6b6e79ddfc
commit 233d53372e
7 changed files with 19850 additions and 0 deletions

19592
FPGA/build/blinky.asc Normal file

File diff suppressed because it is too large Load Diff

BIN
FPGA/build/blinky.bin Normal file

Binary file not shown.

12
FPGA/build/blinky.blif Normal file
View File

@ -0,0 +1,12 @@
# Generated by Yosys 0.9+3521 (git sha1 12132b6850, g++ 9.3.0 -fPIC -Os)
.model top
.inputs key
.outputs led
.names $false
.names $true
1
.names $undef
.names $true led
1 1
.end

26
FPGA/makefile Normal file
View File

@ -0,0 +1,26 @@
# Project setup
PROJ = blinky
BUILD = ./build
DEVICE = 8k
FOOTPRINT = ct256
# Files
FILES = top.v
.PHONY: all clean burn
all:
# if build folder doesn't exist, create it
mkdir -p $(BUILD)
# synthesize using Yosys
yosys -p "synth_ice40 -top top -blif $(BUILD)/$(PROJ).blif" $(FILES)
# Place and route using arachne
arachne-pnr -d $(DEVICE) -P $(FOOTPRINT) -o $(BUILD)/$(PROJ).asc -p pinmap.pcf $(BUILD)/$(PROJ).blif
# Convert to bitstream using IcePack
icepack $(BUILD)/$(PROJ).asc $(BUILD)/$(PROJ).bin
burn:
iceprog $(BUILD)/$(PROJ).bin
clean:
rm build/*

69
FPGA/pinmap.pcf Normal file
View File

@ -0,0 +1,69 @@
# example.pcf
set_io --warn-no-port HW_CLK R9
set_io --warn-no-port LED T15
set_io --warn-no-port KEY T16
# set_io --warn-no-port io0 R6
# set_io --warn-no-port io1 T8
# set_io --warn-no-port io2 T5
# set_io --warn-no-port io3 R9
# set_io --warn-no-port io4 R5
# set_io --warn-no-port io5 T9
# set_io --warn-no-port io6 T3
# set_io --warn-no-port io7 R10
# set_io --warn-no-port io8 R3
# set_io --warn-no-port io9 T10
# set_io --warn-no-port io10 T2
# set_io --warn-no-port io11 T11
# set_io --warn-no-port io12 R2
# set_io --warn-no-port io13 T13
# set_io --warn-no-port io14 T1
# set_io --warn-no-port io15 T14
set_io --warn-no-port ADC_DAT[0] J15
set_io --warn-no-port ADC_DAT[1] K16
set_io --warn-no-port ADC_DAT[2] K15
set_io --warn-no-port ADC_DAT[3] L16
set_io --warn-no-port ADC_DAT[4] M16
set_io --warn-no-port ADC_DAT[5] M15
set_io --warn-no-port ADC_DAT[6] N16
set_io --warn-no-port ADC_DAT[7] P16
set_io --warn-no-port ADC_CLK P15
set_io --warn-no-port FSMC_ADD[0] A9
set_io --warn-no-port FSMC_ADD[1] B9
set_io --warn-no-port FSMC_ADD[2] A10
set_io --warn-no-port FSMC_ADD[3] C10
set_io --warn-no-port FSMC_ADD[4] C9
set_io --warn-no-port FSMC_ADD[5] C8
set_io --warn-no-port FSMC_ADD[6] C7
set_io --warn-no-port FSMC_ADD[7] C11
set_io --warn-no-port FSMC_DAT[0] B10
set_io --warn-no-port FSMC_DAT[1] A11
set_io --warn-no-port FSMC_DAT[2] B11
set_io --warn-no-port FSMC_DAT[3] B12
set_io --warn-no-port FSMC_DAT[4] A1
set_io --warn-no-port FSMC_DAT[5] A2
set_io --warn-no-port FSMC_DAT[6] C3
set_io --warn-no-port FSMC_DAT[7] B3
set_io --warn-no-port FSMC_DAT[8] B4
set_io --warn-no-port FSMC_DAT[9] A5
set_io --warn-no-port FSMC_DAT[10] B5
set_io --warn-no-port FSMC_DAT[11] A6
set_io --warn-no-port FSMC_DAT[12] B6
set_io --warn-no-port FSMC_DAT[13] A7
set_io --warn-no-port FSMC_DAT[14] B7
set_io --warn-no-port FSMC_DAT[15] B8
set_io --warn-no-port FSMC_NL C14
set_io --warn-no-port FSMC_NWAIT B15
set_io --warn-no-port FSMC_NOE B14
set_io --warn-no-port FSMC_NWE A15
set_io --warn-no-port FSMC_NBL[0] C13
set_io --warn-no-port FSMC_NBL[1] C12
set_io --warn-no-port FSMC_NE1 A16
set_io --warn-no-port FSMC_CLK B13

59
FPGA/top.v Normal file
View File

@ -0,0 +1,59 @@
module top (
HW_CLK,
LED,
KEY,
ADC_CLK,
ADC_DAT,
FSMC_CLK,
FSMC_ADD,
FSMC_DAT,
FSMC_NL,
FSMC_NWAIT,
FSMC_NOE,
FSMC_NWE,
FSMC_NBL,
FSMC_NE1
);
/* I/O */
input HW_CLK;
input KEY;
output LED;
input FSMC_NL;
input FSMC_NWAIT;
input FSMC_NOE;
input FSMC_NWE;
input FSMC_NE1;
input [1:0]FSMC_NBL;
input FSMC_CLK;
input [7:0]FSMC_ADD;
output [15:0]FSMC_DAT;
output ADC_CLK;
input [7:0]ADC_DAT;
reg [7:0] adc_result = 8'b0;
/* Counter register */
reg [31:0] counter = 32'b0;
/* LED drivers */
assign LED = counter[24];
// assign LED = ~KEY;
/* always */
always @ (posedge HW_CLK) begin
counter <= counter + 1;
FSMC_DAT = 200;
ADC_CLK = ~ADC_CLK;
end
always @ (posedge ADC_CLK) begin
adc_result = ADC_DAT;
// FSMC_DAT = 200;
end
endmodule

92
src/fpga.rs Normal file
View File

@ -0,0 +1,92 @@
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!("../FPGA/build/blinky.bin");
// 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(())
}