commit
8838cf4f9d
@ -0,0 +1,7 @@ |
||||
build/* |
||||
__pycache__/* |
||||
backup/* |
||||
software/build/* |
||||
helpers/__pycache__/* |
||||
libmodules/__pycache__/* |
||||
NeuralNetworkTest/* |
@ -0,0 +1,28 @@ |
||||
{ |
||||
// Use IntelliSense to learn about possible attributes. |
||||
// Hover to view descriptions of existing attributes. |
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |
||||
"version": "0.2.0", |
||||
"configurations": [ |
||||
{ |
||||
"name": "Python: Current File", |
||||
"type": "python", |
||||
"request": "launch", |
||||
"program": "${file}", |
||||
"args": ["--build", |
||||
"--load", // May be used separately ... |
||||
//"--flash", // May be used separately ... |
||||
"--revision=7.0", |
||||
"--uart-name=crossover", |
||||
//"--with-ethernet", // Not to be used together w/ etherbone! Won't TFTP ... |
||||
"--with-etherbone", |
||||
"--ip-address=192.168.1.20", |
||||
"--csr-csv=build/csr.csv", // Only this one for remotetest.py! |
||||
"--doc" |
||||
], |
||||
//"args": ["--build"], |
||||
//"pythonArgs": ["--build", "--uart-name=crossover"], |
||||
"console": "integratedTerminal" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,3 @@ |
||||
{ |
||||
"python.pythonPath": "/home/kln/fpga/bin/python" |
||||
} |
@ -0,0 +1,298 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
# |
||||
# bfloat16nn.py |
||||
# |
||||
# This file has been derived from LiteX-Boards/colorlight_5b_75x.py |
||||
# Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr> |
||||
# SPDX-License-Identifier: BSD-2-Clause |
||||
# |
||||
# Disclaimer: Still a proof of concept with large timings violations on the IP/UDP and |
||||
# Etherbone stack that need to be optimized. It was initially just used to validate the reversed |
||||
# pinout but happens to work on hardware... |
||||
# |
||||
# History: |
||||
# -------- |
||||
# 21.04.21/KQ Initially derived version |
||||
# |
||||
# Build/Use ---------------------------------------------------------------------------------------- |
||||
# - 'python3 bfloat16nn.py --build --revision=7.0 --uart-name=crossover --with-etherbone --ip-address=192.168.1.20 --csr-csv=build/csr.csv' |
||||
# to generate |
||||
# - 'python3 bfloat16nn.py --load' to download to FPGA |
||||
# - 'ping 192.168.1.20' to verify ethernet connection - via LEFT(!) RJ45 port |
||||
# - 'wishbone-tool --ethernet-host 192.168.1.20 --server terminal --csr-csv build/csr.csv' |
||||
# You should see the LiteX BIOS and be able to interact with it |
||||
# - To load a file to RAM (@0x40000000 len=0x400000) use: |
||||
# wishbone-tool --ethernet-host 192.168.1.20 --server load-file --csr-csv build/csr.csv |
||||
# --load-address 0x40000000 |
||||
# --load-name build/colorlight_5a_75b/software/<filename> |
||||
# To disassemble raw file: |
||||
# ../fpga/litex/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-objdump |
||||
# -D -b binary ./build/colorlight_5a_75b/software/bios/bios.bin -m riscv |
||||
# |
||||
|
||||
import os |
||||
import argparse |
||||
import sys |
||||
import time |
||||
|
||||
from migen import * |
||||
from migen.genlib.misc import WaitTimer |
||||
from migen.genlib.resetsync import AsyncResetSynchronizer |
||||
|
||||
from litex.build.io import DDROutput |
||||
|
||||
from litex_boards.platforms import colorlight_5a_75b |
||||
|
||||
from litex.build.lattice.trellis import trellis_args, trellis_argdict |
||||
|
||||
from litex.soc.cores.clock import * |
||||
from litex.soc.cores.spi_flash import ECP5SPIFlash |
||||
from litex.soc.cores.gpio import GPIOOut |
||||
from litex.soc.integration.soc_core import * |
||||
from litex.soc.integration.builder import * |
||||
|
||||
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField |
||||
from litex.soc.interconnect.stream import SyncFIFO |
||||
|
||||
from litedram.modules import M12L16161A, M12L64322A |
||||
from litedram.phy import GENSDRPHY, HalfRateGENSDRPHY |
||||
from litedram.frontend.dma import LiteDRAMDMAReader, LiteDRAMDMAWriter |
||||
|
||||
from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII |
||||
|
||||
from litex.build.generic_platform import * |
||||
|
||||
import litex.soc.doc as lxsocdoc |
||||
|
||||
from libmodules.dramtransfer import DRAM2FPGA, FPGA2DRAM |
||||
from libmodules.systime import SysTime |
||||
from libmodules.bfloat16nncore import bfloat16NeuralNetworkCore |
||||
|
||||
from helpers.prepare_firmware import copyjob |
||||
|
||||
# CRG ---------------------------------------------------------------------------------------------- |
||||
class _CRG(Module): |
||||
def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll=False, with_rst=True, sdram_rate="1:1"): |
||||
self.clock_domains.cd_sys = ClockDomain() |
||||
if sdram_rate == "1:2": |
||||
self.clock_domains.cd_sys2x = ClockDomain() |
||||
self.clock_domains.cd_sys2x_ps = ClockDomain(reset_less=True) |
||||
else: |
||||
self.clock_domains.cd_sys_ps = ClockDomain(reset_less=True) |
||||
|
||||
# # # |
||||
|
||||
# Clk / Rst |
||||
if not use_internal_osc: |
||||
clk = platform.request("clk25") |
||||
clk_freq = 25e6 |
||||
else: |
||||
clk = Signal() |
||||
div = 5 |
||||
self.specials += Instance("OSCG", |
||||
p_DIV = div, |
||||
o_OSC = clk) |
||||
clk_freq = 310e6/div |
||||
|
||||
rst_n = 1 if not with_rst else platform.request("user_btn_n", 0) |
||||
|
||||
# PLL |
||||
self.submodules.pll = pll = ECP5PLL() |
||||
self.comb += pll.reset.eq(~rst_n) |
||||
pll.register_clkin(clk, clk_freq) |
||||
pll.create_clkout(self.cd_sys, sys_clk_freq) |
||||
if sdram_rate == "1:2": |
||||
pll.create_clkout(self.cd_sys2x, 2*sys_clk_freq) |
||||
pll.create_clkout(self.cd_sys2x_ps, 2*sys_clk_freq, phase=180) # Idealy 90ยฐ but needs to be increased. |
||||
else: |
||||
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=180) # Idealy 90ยฐ but needs to be increased. |
||||
|
||||
# USB PLL |
||||
if with_usb_pll: |
||||
self.submodules.usb_pll = usb_pll = ECP5PLL() |
||||
self.comb += usb_pll.reset.eq(~rst_n) |
||||
usb_pll.register_clkin(clk, clk_freq) |
||||
self.clock_domains.cd_usb_12 = ClockDomain() |
||||
self.clock_domains.cd_usb_48 = ClockDomain() |
||||
usb_pll.create_clkout(self.cd_usb_12, 12e6, margin=0) |
||||
usb_pll.create_clkout(self.cd_usb_48, 48e6, margin=0) |
||||
|
||||
# SDRAM clock |
||||
sdram_clk = ClockSignal("sys2x_ps" if sdram_rate == "1:2" else "sys_ps") |
||||
self.specials += DDROutput(1, 0, platform.request("sdram_clock"), sdram_clk) |
||||
|
||||
# BaseSoC ------------------------------------------------------------------------------------------ |
||||
class BaseSoC(SoCCore): |
||||
|
||||
def __init__(self, board, revision, with_ethernet=False, with_etherbone=False, eth_phy=0, ip_address=None, mac_address=None, sys_clk_freq=60e6, use_internal_osc=False, sdram_rate="1:1", **kwargs): |
||||
platform = colorlight_5a_75b.Platform(revision="7.0") |
||||
|
||||
# SoCCore ---------------------------------------------------------------------------------- |
||||
SoCCore.__init__(self, platform, int(sys_clk_freq), |
||||
ident = "LiteX SoC on Colorlight " + board.upper(), |
||||
ident_version = True, |
||||
**kwargs) |
||||
|
||||
# CRG -------------------------------------------------------------------------------------- |
||||
with_rst = kwargs["uart_name"] not in ["serial", "bridge"] # serial_rx shared with user_btn_n. |
||||
with_usb_pll = kwargs.get("uart_name", None) == "usb_acm" |
||||
self.submodules.crg = _CRG(platform, sys_clk_freq, use_internal_osc=use_internal_osc, with_usb_pll=with_usb_pll,with_rst=with_rst, sdram_rate=sdram_rate) |
||||
|
||||
# SDR SDRAM -------------------------------------------------------------------------------- |
||||
if not self.integrated_main_ram_size: |
||||
sdrphy_cls = HalfRateGENSDRPHY if sdram_rate == "1:2" else GENSDRPHY |
||||
self.submodules.sdrphy = sdrphy_cls(platform.request("sdram")) |
||||
if board == "5a-75e" and revision == "6.0": |
||||
sdram_cls = M12L64322A |
||||
sdram_size = 0x80000000 |
||||
else: |
||||
sdram_cls = M12L16161A |
||||
sdram_size = 0x40000000 |
||||
self.add_sdram("sdram", |
||||
phy = self.sdrphy, |
||||
module = sdram_cls(sys_clk_freq, sdram_rate), |
||||
origin = self.mem_map["main_ram"], |
||||
size = kwargs.get("max_sdram_size", sdram_size), |
||||
l2_cache_size = kwargs.get("l2_size", 8192), |
||||
l2_cache_min_data_width = kwargs.get("min_l2_data_width", 128), |
||||
l2_cache_reverse = True |
||||
) |
||||
|
||||
# Ethernet / Etherbone --------------------------------------------------------------------- |
||||
if with_ethernet or with_etherbone: |
||||
self.submodules.ethphy = LiteEthPHYRGMII( |
||||
clock_pads = self.platform.request("eth_clocks", eth_phy), |
||||
pads = self.platform.request("eth", eth_phy)) |
||||
self.add_csr("ethphy") |
||||
if with_ethernet: |
||||
self.add_ethernet(phy=self.ethphy) |
||||
if with_etherbone: |
||||
self.add_etherbone( |
||||
phy=self.ethphy, |
||||
ip_address = ip_address, |
||||
mac_address = mac_address, |
||||
) |
||||
|
||||
# Base counter (used for clocking) |
||||
counter = Signal(32) # 32-Bit counter |
||||
self.sync += counter.eq(counter + 1) |
||||
|
||||
# System time (count) |
||||
self.submodules.systime = systime = SysTime(comparecount=0x0000EA90) |
||||
self.add_csr("systime") |
||||
|
||||
# DRAM access section |
||||
MAXWORDS = 512 #512 # Transfer length 32 x 32-bit, FIFO depth (511 L1 cache currently possible = 9-bit!) |
||||
# Load unit memory access |
||||
self.submodules.dma_reader = dma_reader = LiteDRAMDMAReader(self.sdram.crossbar.get_port(), fifo_depth=MAXWORDS, fifo_buffered=True) |
||||
dma_reader.add_csr() |
||||
self.add_csr("dma_reader") |
||||
# Load unit transfer |
||||
self.submodules.sync_fifo_in = sync_fifo_in = SyncFIFO([("data", 32)], MAXWORDS, True) |
||||
self.comb += dma_reader.source.connect(sync_fifo_in.sink) # Connect DMA-Reader.source -> FIFO.sink |
||||
# Load unit (LU) |
||||
self.submodules.dram2fpga = dram2fpga = DRAM2FPGA(maxwords=MAXWORDS, dma_reader=dma_reader, sync_fifo=sync_fifo_in) |
||||
self.add_csr("dram2fpga") |
||||
|
||||
""" *** Not used currently ! *** |
||||
MAXWRITEWORDS = 1 # Transfer length 1 x 32-bit = 4 byte maximum (SU) |
||||
# Store unit memory access |
||||
self.submodules.dma_writer = dma_writer = LiteDRAMDMAWriter(self.sdram.crossbar.get_port(), fifo_depth=MAXWRITEWORDS, fifo_buffered=True) |
||||
dma_writer.add_csr() |
||||
self.add_csr("dma_writer") |
||||
# Store unit transfer |
||||
self.submodules.sync_fifo_out = sync_fifo_out = SyncFIFO([("data", 32)], MAXWRITEWORDS, True) |
||||
self.comb += sync_fifo_out.source.connect(dma_writer.sink) # Connect FIFO.source -> DMA-Writer.sink |
||||
# Store unit (SU) |
||||
self.submodules.fpga2dram = fpga2dram = FPGA2DRAM(dma_writer=dma_writer, sync_fifo=sync_fifo_out) |
||||
self.add_csr("fpga2dram") |
||||
""" |
||||
# Integrate bfloat16NN processor |
||||
RAMWAITTIME=1 # Minimum wait! |
||||
self.submodules.bfloat16nn = bfloat16nn = bfloat16NeuralNetworkCore( |
||||
RAMWaitTime=RAMWAITTIME, |
||||
LUCacheSize=MAXWORDS, |
||||
LoadUnit=dram2fpga, |
||||
StoreUnit=None, # *** Not used currently: fpga2dram, |
||||
) |
||||
self.add_csr("bfloat16nn") |
||||
|
||||
# USERLED blink (on-board LED) |
||||
# only w/ uart-name=crossover option: |
||||
if kwargs["uart_name"] not in ["serial", "bridge"]: |
||||
self.comb += platform.request("user_led_n").eq(~(bfloat16nn.bReady)) |
||||
|
||||
# Build -------------------------------------------------------------------------------------------- |
||||
def main(): |
||||
parser = argparse.ArgumentParser(description="LiteX SoC on Colorlight 5A-75X") |
||||
builder_args(parser) |
||||
soc_core_args(parser) |
||||
trellis_args(parser) |
||||
parser.add_argument("--build", action="store_true", help="Build bitstream") |
||||
parser.add_argument("--load", action="store_true", help="Load bitstream") |
||||
parser.add_argument("--board", default="5a-75b", help="Board type: 5a-75b (default) & don't change!") |
||||
parser.add_argument("--revision", default="7.0", type=str, help="Board revision 7.0 (default) & don't change!") |
||||
parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support") |
||||
parser.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support") |
||||
parser.add_argument("--eth-phy", default=0, type=int, help="Ethernet PHY 0 or 1 (default=0)") |
||||
parser.add_argument("--ip-address", default="192.168.1.50", help="Ethernet IP address of the board.") |
||||
parser.add_argument("--mac-address", default="0x726b895bc2e2", help="Ethernet MAC address of the board.") |
||||
parser.add_argument("--sys-clk-freq", default=60e6, type=float, help="System clock frequency (default=60MHz)") |
||||
parser.add_argument("--use-internal-osc", action="store_true", help="Use internal oscillator") |
||||
parser.add_argument("--sdram-rate", default="1:1", help="SDRAM Rate 1:1 Full Rate (default), 1:2 Half Rate") |
||||
parser.add_argument("--csr_csv", default="build/csr.csv", help="CSR list location") |
||||
parser.add_argument("--doc", action="store_true", help="Create doc files for sphinx generator") |
||||
parser.add_argument("--flash", action="store_true", help="Load bitstream to flash") |
||||
args = parser.parse_args() |
||||
|
||||
#assert not (args.with_ethernet and args.with_etherbone) |
||||
soc = BaseSoC(board=args.board, revision=args.revision, |
||||
with_ethernet = args.with_ethernet, |
||||
with_etherbone = args.with_etherbone, |
||||
eth_phy = args.eth_phy, |
||||
ip_address = args.ip_address, |
||||
mac_address = int(args.mac_address, 0), |
||||
sys_clk_freq = args.sys_clk_freq, |
||||
use_internal_osc = args.use_internal_osc, |
||||
sdram_rate = args.sdram_rate, |
||||
**soc_core_argdict(args)) |
||||
|
||||
# 32MBit SPIFlash ------------------------------------------------------------------------ |
||||
flashbase = 0xc0000000 |
||||
flashoffset = 0x100000 # Used to be zero (default) |
||||
soc.mem_map["spiflash"] = flashbase # Length: 0x01000000 ('til 0xc1000000 - 1) |
||||
# Boot at +1MB |
||||
soc.add_constant("FLASH_BOOT_ADDRESS", soc.mem_map["spiflash"] + 1024*1024) # 0xc0100000 |
||||
soc.add_spi_flash(name="spiflash", mode="1x", dummy_cycles=8, clk_freq=5e6) |
||||
|
||||
builder = Builder(soc, **builder_argdict(args)) |
||||
# Now override boot address (used to be zero/default) |
||||
args.ecppack_bootaddr = flashbase + flashoffset # 0xC0100000 |
||||
builder.build(**trellis_argdict(args), run=args.build) # Written here to (local) build tree |
||||
|
||||
if args.doc: |
||||
print("Generating documentation for sphinx ...") |
||||
lxsocdoc.generate_docs(soc, "build/documentation/", project_name="neopixelar", author="KQ") |
||||
print("Generate via: 'sphinx-build -b html build/documentation build/documentation/html'") |
||||
|
||||
if args.load: |
||||
prog = soc.platform.create_programmer() |
||||
prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".svf")) |
||||
return |
||||
|
||||
if args.flash: # Convert Bit File to Jtag Write Flash command |
||||
name = os.path.join(builder.gateware_dir, soc.build_name) |
||||
print(f"Executing ./bit_to_flash.py {name}.bit {name}.svf.flash") |
||||
from helpers.bit_to_flash import convertBitToFlashFile |
||||
convertBitToFlashFile(name + ".bit", name + ".svf.flash", address=0) |
||||
from helpers.load_to_flash import load2flash |
||||
load2flash(name + ".svf.flash") |
||||
return |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
starttime = time.time() |
||||
copyjob() # Create backup if nec. & move our firmware to the correct location |
||||
main() # Create FPGA & load/flash |
||||
print("Time used: {0} min.".format(int((time.time() - starttime)/60.0))) |
@ -0,0 +1,9 @@ |
||||
# FIRMWARE # |
||||
|
||||
This directory contains the modified versions of several BIOS related files. |
||||
The modifications are: |
||||
|
||||
1. boot.c - doRAMBoot introduced (permitting RAM boot - automatically selecting the currently inactive RAM bank) |
||||
2. cmd_mem.c - additional commands dumpregs & ramboot introduced (mx, mhsig no longer used) |
||||
3. console.c - non-blocking feature implemented (set_console, kbhit introduced, putchar modified) |
||||
4. main.c - BIOS starter, modified for non-blocking operation |
@ -0,0 +1,688 @@ |
||||
// This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
// This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
|
||||
// This file is Copyright (c) 2018 Ewen McNeill <ewen@naos.co.nz>
|
||||
// This file is Copyright (c) 2018 Felix Held <felix-github@felixheld.de>
|
||||
// This file is Copyright (c) 2019 Gabriel L. Somlo <gsomlo@gmail.com>
|
||||
// This file is Copyright (c) 2017 Tim 'mithro' Ansell <mithro@mithis.com>
|
||||
// This file is Copyright (c) 2018 William D. Jones <thor0505@comcast.net>
|
||||
// License: BSD
|
||||
|
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <console.h> |
||||
#include <uart.h> |
||||
#include <system.h> |
||||
#include <crc.h> |
||||
#include <string.h> |
||||
#include <irq.h> |
||||
|
||||
#include <generated/mem.h> |
||||
#include <generated/csr.h> |
||||
#include <generated/soc.h> |
||||
|
||||
#include "sfl.h" |
||||
#include "boot.h" |
||||
#include "jsmn.h" |
||||
|
||||
#include <progress.h> |
||||
#include <spiflash.h> |
||||
|
||||
#include <libliteeth/udp.h> |
||||
#include <libliteeth/tftp.h> |
||||
|
||||
#include <liblitesdcard/spisdcard.h> |
||||
#include <liblitesdcard/sdcard.h> |
||||
#include <liblitesdcard/fat/ff.h> |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Helpers */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y)) |
||||
#define min(x, y) (((x) < (y)) ? (x) : (y)) |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
extern void boot_helper(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr); |
||||
|
||||
static void __attribute__((noreturn)) boot(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr) |
||||
{ |
||||
//printf("Executing booted program at 0x%08x\n\n", addr);
|
||||
//printf("--============= \e[1mLiftoff!\e[0m ===============--\n");
|
||||
//uart_sync(); // This is a 'boot-locker'!
|
||||
#ifdef CONFIG_CPU_HAS_INTERRUPT |
||||
irq_setmask(0); |
||||
irq_setie(0); |
||||
#endif |
||||
flush_cpu_icache(); |
||||
flush_cpu_dcache(); |
||||
#ifdef CONFIG_L2_SIZE |
||||
flush_l2_cache(); |
||||
#endif |
||||
|
||||
#if defined(CONFIG_CPU_TYPE_MOR1KX) && defined(CONFIG_CPU_VARIANT_LINUX) |
||||
/* Mainline Linux expects to have exception vector base address set to the
|
||||
* base address of Linux kernel; it also expects to be run with an offset |
||||
* of 0x100. */ |
||||
mtspr(SPR_EVBAR, addr); |
||||
addr += 0x100; |
||||
#endif |
||||
|
||||
boot_helper(r1, r2, r3, addr); |
||||
while(1); |
||||
} |
||||
|
||||
enum { |
||||
ACK_TIMEOUT, |
||||
ACK_CANCELLED, |
||||
ACK_OK |
||||
}; |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* ROM Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#ifdef ROM_BOOT_ADDRESS |
||||
/* Running the application code from ROM is the fastest way to execute code
|
||||
and could be interesting when the code is small enough, on large devices |
||||
where many blockrams are available or simply when the execution speed is |
||||
critical. Defining ROM_BOOT_ADDRESS in the SoC will make the BIOS jump to |
||||
it at boot. */ |
||||
void romboot(void) |
||||
{ |
||||
boot(0, 0, 0, ROM_BOOT_ADDRESS); |
||||
} |
||||
#endif |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Serial Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
static int check_ack(void) |
||||
{ |
||||
int recognized; |
||||
static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK; |
||||
|
||||
timer0_en_write(0); |
||||
timer0_reload_write(0); |
||||
timer0_load_write(CONFIG_CLOCK_FREQUENCY/4); |
||||
timer0_en_write(1); |
||||
timer0_update_value_write(1); |
||||
recognized = 0; |
||||
while(timer0_value_read()) { |
||||
if(uart_read_nonblock()) { |
||||
char c; |
||||
c = uart_read(); |
||||
if((c == 'Q') || (c == '\e')) |
||||
return ACK_CANCELLED; |
||||
if(c == str[recognized]) { |
||||
recognized++; |
||||
if(recognized == SFL_MAGIC_LEN) |
||||
return ACK_OK; |
||||
} else { |
||||
if(c == str[0]) |
||||
recognized = 1; |
||||
else |
||||
recognized = 0; |
||||
} |
||||
} |
||||
timer0_update_value_write(1); |
||||
} |
||||
return ACK_TIMEOUT; |
||||
} |
||||
|
||||
static uint32_t get_uint32(unsigned char* data) |
||||
{ |
||||
return ((uint32_t) data[0] << 24) | |
||||
((uint32_t) data[1] << 16) | |
||||
((uint32_t) data[2] << 8) | |
||||
(uint32_t) data[3]; |
||||
} |
||||
|
||||
#define MAX_FAILED 5 |
||||
|
||||
/* Returns 1 if other boot methods should be tried */ |
||||
int serialboot(void) |
||||
{ |
||||
struct sfl_frame frame; |
||||
int failed; |
||||
static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ; |
||||
const char *c; |
||||
int ack_status; |
||||
|
||||
printf("Booting from serial...\n"); |
||||
printf("Press Q or ESC to abort boot completely.\n"); |
||||
|
||||
/* Send the serialboot "magic" request to Host */ |
||||
c = str; |
||||
while(*c) { |
||||
uart_write(*c); |
||||
c++; |
||||
} |
||||
ack_status = check_ack(); |
||||
if(ack_status == ACK_TIMEOUT) { |
||||
printf("Timeout\n"); |
||||
return 1; |
||||
} |
||||
if(ack_status == ACK_CANCELLED) { |
||||
printf("Cancelled\n"); |
||||
return 0; |
||||
} |
||||
/* Assume ACK_OK */ |
||||
|
||||
failed = 0; |
||||
while(1) { |
||||
int i; |
||||
int actualcrc; |
||||
int goodcrc; |
||||
|
||||
/* Get one Frame */ |
||||
frame.payload_length = uart_read(); |
||||
frame.crc[0] = uart_read(); |
||||
frame.crc[1] = uart_read(); |
||||
frame.cmd = uart_read(); |
||||
for(i=0;i<frame.payload_length;i++) |
||||
frame.payload[i] = uart_read(); |
||||
|
||||
/* Check Frame CRC (if CMD has a CRC) */ |
||||
if (frame.cmd != SFL_CMD_LOAD_NO_CRC) { |
||||
actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1]; |
||||
goodcrc = crc16(&frame.cmd, frame.payload_length+1); |
||||
if(actualcrc != goodcrc) { |
||||
/* Clear out the RX buffer */ |
||||
while (uart_read_nonblock()) uart_read(); |
||||
failed++; |
||||
if(failed == MAX_FAILED) { |
||||
printf("Too many consecutive errors, aborting"); |
||||
return 1; |
||||
} |
||||
uart_write(SFL_ACK_CRCERROR); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
/* Execute Frame CMD */ |
||||
switch(frame.cmd) { |
||||
case SFL_CMD_ABORT: |
||||
failed = 0; |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
return 1; |
||||
case SFL_CMD_LOAD: |
||||
case SFL_CMD_LOAD_NO_CRC: { |
||||
char *writepointer; |
||||
|
||||
failed = 0; |
||||
writepointer = (char *) get_uint32(&frame.payload[0]); |
||||
for(i=4;i<frame.payload_length;i++) |
||||
*(writepointer++) = frame.payload[i]; |
||||
if (frame.cmd == SFL_CMD_LOAD) |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
break; |
||||
} |
||||
case SFL_CMD_JUMP: { |
||||
uint32_t addr; |
||||
|
||||
failed = 0; |
||||
addr = get_uint32(&frame.payload[0]); |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
boot(0, 0, 0, addr); |
||||
break; |
||||
} |
||||
case SFL_CMD_FLASH: { |
||||
#if (defined CSR_SPIFLASH_BASE && defined SPIFLASH_PAGE_SIZE) |
||||
uint32_t addr; |
||||
|
||||
failed = 0; |
||||
addr = get_uint32(&frame.payload[0]); |
||||
|
||||
for (i = 4; i < frame.payload_length; i++) { |
||||
/* Erase page at sector boundaries before writing */ |
||||
if ((addr & (SPIFLASH_SECTOR_SIZE - 1)) == 0) { |
||||
erase_flash_sector(addr); |
||||
} |
||||
write_to_flash(addr, &frame.payload[i], 1); |
||||
addr++; |
||||
} |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
#endif |
||||
break; |
||||
} |
||||
case SFL_CMD_REBOOT: |
||||
#ifdef CSR_CTRL_RESET_ADDR |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
ctrl_reset_write(1); |
||||
#endif |
||||
break; |
||||
default: |
||||
failed++; |
||||
if(failed == MAX_FAILED) { |
||||
printf("Too many consecutive errors, aborting"); |
||||
return 1; |
||||
} |
||||
uart_write(SFL_ACK_UNKNOWN); |
||||
break; |
||||
} |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Ethernet Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#ifdef CSR_ETHMAC_BASE |
||||
|
||||
#ifndef LOCALIP1 |
||||
#define LOCALIP1 192 |
||||
#define LOCALIP2 168 |
||||
#define LOCALIP3 1 |
||||
#define LOCALIP4 20 // KQ
|
||||
#endif |
||||
|
||||
#ifndef REMOTEIP1 |
||||
#define REMOTEIP1 192 |
||||
#define REMOTEIP2 168 |
||||
#define REMOTEIP3 1 |
||||
#define REMOTEIP4 11 // KQ
|
||||
#endif |
||||
|
||||
#ifndef TFTP_SERVER_PORT |
||||
#define TFTP_SERVER_PORT 69 |
||||
#endif |
||||
|
||||
static const unsigned char macadr[6] = {0x10, 0xe2, 0xd5, 0x00, 0x00, 0x00}; |
||||
|
||||
static int copy_file_from_tftp_to_ram(unsigned int ip, unsigned short server_port, |
||||
const char *filename, char *buffer) |
||||
{ |
||||
int size; |
||||
printf("Copying %s to 0x%08x... ", filename, buffer); |
||||
size = tftp_get(ip, server_port, filename, buffer); |
||||
if(size > 0) |
||||
printf("(%d bytes)", size); |
||||
printf("\n"); |
||||
return size; |
||||
} |
||||
|
||||
static void netboot_from_json(const char * filename, unsigned int ip, unsigned short tftp_port) |
||||
{ |
||||
int size; |
||||
uint8_t i; |
||||
uint8_t count; |
||||
|
||||
/* FIXME: modify/increase if too limiting */ |
||||
char json_buffer[1024]; |
||||
char json_name[32]; |
||||
char json_value[32]; |
||||
|
||||
unsigned long boot_r1 = 0; |
||||
unsigned long boot_r2 = 0; |
||||
unsigned long boot_r3 = 0; |
||||
unsigned long boot_addr = 0; |
||||
|
||||
uint8_t image_found = 0; |
||||
uint8_t boot_addr_found = 0; |
||||
|
||||
/* Read JSON file */ |
||||
size = tftp_get(ip, tftp_port, filename, json_buffer); |
||||
if (size <= 0) |
||||
return; |
||||
|
||||
/* Parse JSON file */ |
||||
jsmntok_t t[32]; |
||||
jsmn_parser p; |
||||
jsmn_init(&p); |
||||
count = jsmn_parse(&p, json_buffer, strlen(json_buffer), t, sizeof(t)/sizeof(*t)); |
||||
for (i=0; i<count-1; i++) { |
||||
memset(json_name, 0, sizeof(json_name)); |
||||
memset(json_value, 0, sizeof(json_value)); |
||||
/* Elements are JSON strings with 1 children */ |
||||
if ((t[i].type == JSMN_STRING) && (t[i].size == 1)) { |
||||
/* Get Element's filename */ |
||||
memcpy(json_name, json_buffer + t[i].start, t[i].end - t[i].start); |
||||
/* Get Element's address */ |
||||
memcpy(json_value, json_buffer + t[i+1].start, t[i+1].end - t[i+1].start); |
||||
/* Skip bootargs (optional) */ |
||||
if (strncmp(json_name, "bootargs", 8) == 0) { |
||||
continue; |
||||
} |
||||
/* Get boot addr (optional) */ |
||||
else if (strncmp(json_name, "addr", 4) == 0) { |
||||
boot_addr = strtoul(json_value, NULL, 0); |
||||
boot_addr_found = 1; |
||||
} |
||||
/* Get boot r1 (optional) */ |
||||
else if (strncmp(json_name, "r1", 2) == 0) { |
||||
memcpy(json_name, json_buffer + t[i].start, t[i].end - t[i].start); |
||||
boot_r1 = strtoul(json_value, NULL, 0); |
||||
} |
||||
/* Get boot r2 (optional) */ |
||||
else if (strncmp(json_name, "r2", 2) == 0) { |
||||
boot_r2 = strtoul(json_value, NULL, 0); |
||||
} |
||||
/* Get boot r3 (optional) */ |
||||
else if (strncmp(json_name, "r3", 2) == 0) { |
||||
boot_r3 = strtoul(json_value, NULL, 0); |
||||
/* Copy Image from Network to address */ |
||||
} else { |
||||
size = copy_file_from_tftp_to_ram(ip, tftp_port, json_name, (void *)strtoul(json_value, NULL, 0)); |
||||
if (size <= 0) |
||||
return; |
||||
image_found = 1; |
||||
if (boot_addr_found == 0) /* Boot to last Image address if no bootargs.addr specified */ |
||||
boot_addr = strtoul(json_value, NULL, 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Boot */ |
||||
if (image_found) |
||||
boot(boot_r1, boot_r2, boot_r3, boot_addr); |
||||
} |
||||
|
||||
static void netboot_from_bin(const char * filename, unsigned int ip, unsigned short tftp_port) |
||||
{ |
||||
int size; |
||||
size = copy_file_from_tftp_to_ram(ip, tftp_port, filename, (void *)MAIN_RAM_BASE); |
||||
if (size <= 0) |
||||
return; |
||||
boot(0, 0, 0, MAIN_RAM_BASE); |
||||
} |
||||
|
||||
void netboot(void) |
||||
{ |
||||
unsigned int ip; |
||||
|
||||
|
||||
printf("Booting from network...\n"); |
||||
printf("Local IP : %d.%d.%d.%d\n", LOCALIP1, LOCALIP2, LOCALIP3, LOCALIP4); |
||||
printf("Remote IP: %d.%d.%d.%d\n", REMOTEIP1, REMOTEIP2, REMOTEIP3, REMOTEIP4); |
||||
|
||||
ip = IPTOINT(REMOTEIP1, REMOTEIP2, REMOTEIP3, REMOTEIP4); |
||||
udp_start(macadr, IPTOINT(LOCALIP1, LOCALIP2, LOCALIP3, LOCALIP4)); |
||||
|
||||
/* Boot from boot.json */ |
||||
printf("Booting from boot.json...\n"); |
||||
netboot_from_json("boot.json", ip, TFTP_SERVER_PORT); |
||||
|
||||
/* Boot from boot.bin */ |
||||
printf("Booting from boot.bin...\n"); |
||||
netboot_from_bin("boot.bin", ip, TFTP_SERVER_PORT); |
||||
|
||||
/* Boot failed if we are here... */ |
||||
printf("Network boot failed.\n"); |
||||
} |
||||
#endif |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Flash Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#ifdef FLASH_BOOT_ADDRESS |
||||
|
||||
static unsigned int check_image_in_flash(unsigned int base_address) |
||||
{ |
||||
uint32_t length; |
||||
uint32_t crc; |
||||
uint32_t got_crc; |
||||
|
||||
length = MMPTR(base_address); |
||||
if((length < 32) || (length > 16*1024*1024)) { |
||||
printf("Error: Invalid image length 0x%08x\n", length); |
||||
return 0; |
||||
} |
||||
|
||||
crc = MMPTR(base_address + 4); |
||||
got_crc = crc32((unsigned char *)(base_address + 8), length); |
||||
if(crc != got_crc) { |
||||
printf("CRC failed (expected %08x, got %08x)\n", crc, got_crc); |
||||
return 0; |
||||
} |
||||
|
||||
return length; |
||||
} |
||||
|
||||
#if defined(MAIN_RAM_BASE) && defined(FLASH_BOOT_ADDRESS) |
||||
static int copy_image_from_flash_to_ram(unsigned int flash_address, unsigned long ram_address) |
||||
{ |
||||
uint32_t length; |
||||
uint32_t offset; |
||||
|
||||
length = check_image_in_flash(flash_address); |
||||
if(length > 0) { |
||||
printf("Copying 0x%08x to 0x%08x (%d bytes)...\n", flash_address, ram_address, length); |
||||
offset = 0; |
||||
//init_progression_bar(length);
|
||||
while (length > 0) { |
||||
uint32_t chunk_length; |
||||
chunk_length = min(length, 0x8000); /* 32KB chunks */ |
||||
memcpy((void *) ram_address + offset, (void*) flash_address + offset + 8, chunk_length); |
||||
offset += chunk_length; |
||||
length -= chunk_length; |
||||
//show_progress(offset);
|
||||
} |
||||
//show_progress(offset);
|
||||
//printf("\n");
|
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
#endif |
||||
|
||||
void flashboot(void) |
||||
{ |
||||
uint32_t length; |
||||
uint32_t result; |
||||
|
||||
printf("Booting from flash...\n"); |
||||
length = check_image_in_flash(FLASH_BOOT_ADDRESS); |
||||
if(!length) |
||||
return; |
||||
|
||||
#ifdef MAIN_RAM_BASE |
||||
/* When Main RAM is available, copy the code from the Flash and execute it
|
||||
from Main RAM since faster */ |
||||
result = copy_image_from_flash_to_ram(FLASH_BOOT_ADDRESS, MAIN_RAM_BASE); |
||||
if(!result) |
||||
return; |
||||
boot(0, 0, 0, MAIN_RAM_BASE); |
||||
#else |
||||
/* When Main RAM is not available, execute the code directly from Flash (XIP).
|
||||
The code starts after (a) length and (b) CRC -- both uint32_t */ |
||||
boot(0, 0, 0, (FLASH_BOOT_ADDRESS + 2 * sizeof(uint32_t))); |
||||
#endif |
||||
} |
||||
|
||||
#endif |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* SDCard Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#if defined(CSR_SPISDCARD_BASE) || defined(CSR_SDCORE_BASE) |
||||
|
||||
static int copy_file_from_sdcard_to_ram(const char * filename, unsigned long ram_address) |
||||
{ |
||||
FRESULT fr; |
||||
FATFS fs; |
||||
FIL file; |
||||
uint32_t br; |
||||
uint32_t offset; |
||||
uint32_t length; |
||||
|
||||
fr = f_mount(&fs, "", 1); |
||||
if (fr != FR_OK) |
||||
return 0; |
||||
fr = f_open(&file, filename, FA_READ); |
||||
if (fr != FR_OK) { |
||||
printf("%s file not found.\n", filename); |
||||
f_mount(0, "", 0); |
||||
return 0; |
||||
} |
||||
|
||||
length = f_size(&file); |
||||
printf("Copying %s to 0x%08x (%d bytes)...\n", filename, ram_address, length); |
||||
init_progression_bar(length); |
||||
offset = 0; |
||||
for (;;) { |
||||
fr = f_read(&file, (void*) ram_address + offset, 0x8000, &br); |
||||
if (fr != FR_OK) { |
||||
printf("file read error.\n"); |
||||
f_close(&file); |
||||
f_mount(0, "", 0); |
||||
return 0; |
||||
} |
||||
if (br == 0) |
||||
break; |
||||
offset += br; |
||||
show_progress(offset); |
||||
} |
||||
show_progress(offset); |
||||
printf("\n"); |
||||
|
||||
f_close(&file); |
||||
f_mount(0, "", 0); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static void sdcardboot_from_json(const char * filename) |
||||
{ |
||||
FRESULT fr; |
||||
FATFS fs; |
||||
FIL file; |
||||
|
||||
uint8_t i; |
||||
uint8_t count; |
||||
uint32_t length; |
||||
uint32_t result; |
||||
|
||||
/* FIXME: modify/increase if too limiting */ |
||||
char json_buffer[1024]; |
||||
char json_name[32]; |
||||
char json_value[32]; |
||||
|
||||
unsigned long boot_r1 = 0; |
||||
unsigned long boot_r2 = 0; |
||||
unsigned long boot_r3 = 0; |
||||
unsigned long boot_addr = 0; |
||||
|
||||
uint8_t image_found = 0; |
||||
uint8_t boot_addr_found = 0; |
||||
|
||||
/* Read JSON file */ |
||||
fr = f_mount(&fs, "", 1); |
||||
if (fr != FR_OK) |
||||
return; |
||||
fr = f_open(&file, filename, FA_READ); |
||||
if (fr != FR_OK) { |
||||
printf("%s file not found.\n", filename); |
||||
f_mount(0, "", 0); |
||||
return; |
||||
} |
||||
|
||||
fr = f_read(&file, json_buffer, sizeof(json_buffer), &length); |
||||
|
||||
/* Close JSON file */ |
||||
f_close(&file); |
||||
f_mount(0, "", 0); |
||||
|
||||
/* Parse JSON file */ |
||||
jsmntok_t t[32]; |
||||
jsmn_parser p; |
||||
jsmn_init(&p); |
||||
count = jsmn_parse(&p, json_buffer, strlen(json_buffer), t, sizeof(t)/sizeof(*t)); |
||||
for (i=0; i<count-1; i++) { |
||||
memset(json_name, 0, sizeof(json_name)); |
||||
memset(json_value, 0, sizeof(json_value)); |
||||
/* Elements are JSON strings with 1 children */ |
||||
if ((t[i].type == JSMN_STRING) && (t[i].size == 1)) { |
||||
/* Get Element's filename */ |
||||
memcpy(json_name, json_buffer + t[i].start, t[i].end - t[i].start); |
||||
/* Get Element's address */ |
||||
memcpy(json_value, json_buffer + t[i+1].start, t[i+1].end - t[i+1].start); |
||||
/* Skip bootargs (optional) */ |
||||
if (strncmp(json_name, "bootargs", 8) == 0) { |
||||
continue; |
||||
} |
||||
/* Get boot addr (optional) */ |
||||
else if (strncmp(json_name, "addr", 4) == 0) { |
||||
boot_addr = strtoul(json_value, NULL, 0); |
||||
boot_addr_found = 1; |
||||
} |
||||
/* Get boot r1 (optional) */ |
||||
else if (strncmp(json_name, "r1", 2) == 0) { |
||||
memcpy(json_name, json_buffer + t[i].start, t[i].end - t[i].start); |
||||
boot_r1 = strtoul(json_value, NULL, 0); |
||||
} |
||||
/* Get boot r2 (optional) */ |
||||
else if (strncmp(json_name, "r2", 2) == 0) { |
||||
boot_r2 = strtoul(json_value, NULL, 0); |
||||
} |
||||
/* Get boot r3 (optional) */ |
||||
else if (strncmp(json_name, "r3", 2) == 0) { |
||||
boot_r3 = strtoul(json_value, NULL, 0); |
||||
/* Copy Image from SDCard to address */ |
||||
} else { |
||||
result = copy_file_from_sdcard_to_ram(json_name, strtoul(json_value, NULL, 0)); |
||||
if (result == 0) |
||||
return; |
||||
image_found = 1; |
||||
if (boot_addr_found == 0) /* Boot to last Image address if no bootargs.addr specified */ |
||||
boot_addr = strtoul(json_value, NULL, 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Boot */ |
||||
if (image_found) |
||||
boot(boot_r1, boot_r2, boot_r3, boot_addr); |
||||
} |
||||
|
||||
static void sdcardboot_from_bin(const char * filename) |
||||
{ |
||||
uint32_t result; |
||||
result = copy_file_from_sdcard_to_ram(filename, MAIN_RAM_BASE); |
||||
if (result == 0) |
||||
return; |
||||
boot(0, 0, 0, MAIN_RAM_BASE); |
||||
} |
||||
|
||||
void sdcardboot(void) |
||||
{ |
||||
#ifdef CSR_SPISDCARD_BASE |
||||
printf("Booting from SDCard in SPI-Mode...\n"); |
||||
#endif |
||||
#ifdef CSR_SDCORE_BASE |
||||
printf("Booting from SDCard in SD-Mode...\n"); |
||||
#endif |
||||
|
||||
/* Boot from boot.json */ |
||||
printf("Booting from boot.json...\n"); |
||||
sdcardboot_from_json("boot.json"); |
||||
|
||||
/* Boot from boot.bin */ |
||||
printf("Booting from boot.bin...\n"); |
||||
sdcardboot_from_bin("boot.bin"); |
||||
|
||||
/* Boot failed if we are here... */ |
||||
printf("SDCard boot failed.\n"); |
||||
} |
||||
#endif |
||||
|
||||
#define RAM_BOOT_OPTION 1 |
||||
#ifdef RAM_BOOT_OPTION |
||||
extern void doRAMboot(int ramno); |
||||
void doRAMboot(int ramno) |
||||
{ |
||||
printf("Trying RAM boot [%d]...\n", ramno); |
||||
if(ramno == 2) |
||||
boot(0, 0, 0, MAIN_RAM_BASE + (MAIN_RAM_SIZE / 2));
|
||||
else |
||||
boot(0, 0, 0, MAIN_RAM_BASE);
|
||||
} |
||||
#endif |
@ -0,0 +1,684 @@ |
||||
// This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr> |
||||
// This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk> |
||||
// This file is Copyright (c) 2018 Ewen McNeill <ewen@naos.co.nz> |
||||
// This file is Copyright (c) 2018 Felix Held <felix-github@felixheld.de> |
||||
// This file is Copyright (c) 2019 Gabriel L. Somlo <gsomlo@gmail.com> |
||||
// This file is Copyright (c) 2017 Tim 'mithro' Ansell <mithro@mithis.com> |
||||
// This file is Copyright (c) 2018 William D. Jones <thor0505@comcast.net> |
||||
// License: BSD |
||||
|
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <console.h> |
||||
#include <uart.h> |
||||
#include <system.h> |
||||
#include <crc.h> |
||||
#include <string.h> |
||||
#include <irq.h> |
||||
|
||||
#include <generated/mem.h> |
||||
#include <generated/csr.h> |
||||
#include <generated/soc.h> |
||||
|
||||
#include "sfl.h" |
||||
#include "boot.h" |
||||
#include "jsmn.h" |
||||
|
||||
#include <progress.h> |
||||
#include <spiflash.h> |
||||
|
||||
#include <libliteeth/udp.h> |
||||
#include <libliteeth/tftp.h> |
||||
|
||||
#include <liblitesdcard/spisdcard.h> |
||||
#include <liblitesdcard/sdcard.h> |
||||
#include <liblitesdcard/fat/ff.h> |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Helpers */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y)) |
||||
#define min(x, y) (((x) < (y)) ? (x) : (y)) |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
extern void boot_helper(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr); |
||||
|
||||
static void __attribute__((noreturn)) boot(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr) |
||||
{ |
||||
printf("Executing booted program at 0x%08x\n\n", addr); |
||||
printf("--============= \e[1mLiftoff!\e[0m ===============--\n"); |
||||
uart_sync(); |
||||
#ifdef CONFIG_CPU_HAS_INTERRUPT |
||||
irq_setmask(0); |
||||
irq_setie(0); |
||||
#endif |
||||
flush_cpu_icache(); |
||||
flush_cpu_dcache(); |
||||
#ifdef CONFIG_L2_SIZE |
||||
flush_l2_cache(); |
||||
#endif |
||||
|
||||
#if defined(CONFIG_CPU_TYPE_MOR1KX) && defined(CONFIG_CPU_VARIANT_LINUX) |
||||
/* Mainline Linux expects to have exception vector base address set to the |
||||
* base address of Linux kernel; it also expects to be run with an offset |
||||
* of 0x100. */ |
||||
mtspr(SPR_EVBAR, addr); |
||||
addr += 0x100; |
||||
#endif |
||||
|
||||
boot_helper(r1, r2, r3, addr); |
||||
while(1); |
||||
} |
||||
|
||||
enum { |
||||
ACK_TIMEOUT, |
||||
ACK_CANCELLED, |
||||
ACK_OK |
||||
}; |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* ROM Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#ifdef ROM_BOOT_ADDRESS |
||||
/* Running the application code from ROM is the fastest way to execute code |
||||
and could be interesting when the code is small enough, on large devices |
||||
where many blockrams are available or simply when the execution speed is |
||||
critical. Defining ROM_BOOT_ADDRESS in the SoC will make the BIOS jump to |
||||
it at boot. */ |
||||
void romboot(void) |
||||
{ |
||||
boot(0, 0, 0, ROM_BOOT_ADDRESS); |
||||
} |
||||
#endif |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Serial Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
static int check_ack(void) |
||||
{ |
||||
int recognized; |
||||
static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK; |
||||
|
||||
timer0_en_write(0); |
||||
timer0_reload_write(0); |
||||
timer0_load_write(CONFIG_CLOCK_FREQUENCY/4); |
||||
timer0_en_write(1); |
||||
timer0_update_value_write(1); |
||||
recognized = 0; |
||||
while(timer0_value_read()) { |
||||
if(uart_read_nonblock()) { |
||||
char c; |
||||
c = uart_read(); |
||||
if((c == 'Q') || (c == '\e')) |
||||
return ACK_CANCELLED; |
||||
if(c == str[recognized]) { |
||||
recognized++; |
||||
if(recognized == SFL_MAGIC_LEN) |
||||
return ACK_OK; |
||||
} else { |
||||
if(c == str[0]) |
||||
recognized = 1; |
||||
else |
||||
recognized = 0; |
||||
} |
||||
} |
||||
timer0_update_value_write(1); |
||||
} |
||||
return ACK_TIMEOUT; |
||||
} |
||||
|
||||
static uint32_t get_uint32(unsigned char* data) |
||||
{ |
||||
return ((uint32_t) data[0] << 24) | |
||||
((uint32_t) data[1] << 16) | |
||||
((uint32_t) data[2] << 8) | |
||||
(uint32_t) data[3]; |
||||
} |
||||
|
||||
#define MAX_FAILED 5 |
||||
|
||||
/* Returns 1 if other boot methods should be tried */ |
||||
int serialboot(void) |
||||
{ |
||||
struct sfl_frame frame; |
||||
int failed; |
||||
static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ; |
||||
const char *c; |
||||
int ack_status; |
||||
|
||||
printf("Booting from serial...\n"); |
||||
printf("Press Q or ESC to abort boot completely.\n"); |
||||
|
||||
/* Send the serialboot "magic" request to Host */ |
||||
c = str; |
||||
while(*c) { |
||||
uart_write(*c); |
||||
c++; |
||||
} |
||||
ack_status = check_ack(); |
||||
if(ack_status == ACK_TIMEOUT) { |
||||
printf("Timeout\n"); |
||||
return 1; |
||||
} |
||||
if(ack_status == ACK_CANCELLED) { |
||||
printf("Cancelled\n"); |
||||
return 0; |
||||
} |
||||
/* Assume ACK_OK */ |
||||
|
||||
failed = 0; |
||||
while(1) { |
||||
int i; |
||||
int actualcrc; |
||||
int goodcrc; |
||||
|
||||
/* Get one Frame */ |
||||
frame.payload_length = uart_read(); |
||||
frame.crc[0] = uart_read(); |
||||
frame.crc[1] = uart_read(); |
||||
frame.cmd = uart_read(); |
||||
for(i=0;i<frame.payload_length;i++) |
||||
frame.payload[i] = uart_read(); |
||||
|
||||
/* Check Frame CRC (if CMD has a CRC) */ |
||||
if (frame.cmd != SFL_CMD_LOAD_NO_CRC) { |
||||
actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1]; |
||||
goodcrc = crc16(&frame.cmd, frame.payload_length+1); |
||||
if(actualcrc != goodcrc) { |
||||
/* Clear out the RX buffer */ |
||||
while (uart_read_nonblock()) uart_read(); |
||||
failed++; |
||||
if(failed == MAX_FAILED) { |
||||
printf("Too many consecutive errors, aborting"); |
||||
return 1; |
||||
} |
||||
uart_write(SFL_ACK_CRCERROR); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
/* Execute Frame CMD */ |
||||
switch(frame.cmd) { |
||||
case SFL_CMD_ABORT: |
||||
failed = 0; |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
return 1; |
||||
case SFL_CMD_LOAD: |
||||
case SFL_CMD_LOAD_NO_CRC: { |
||||
char *writepointer; |
||||
|
||||
failed = 0; |
||||
writepointer = (char *) get_uint32(&frame.payload[0]); |
||||
for(i=4;i<frame.payload_length;i++) |
||||
*(writepointer++) = frame.payload[i]; |
||||
if (frame.cmd == SFL_CMD_LOAD) |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
break; |
||||
} |
||||
case SFL_CMD_JUMP: { |
||||
uint32_t addr; |
||||
|
||||
failed = 0; |
||||
addr = get_uint32(&frame.payload[0]); |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
boot(0, 0, 0, addr); |
||||
break; |
||||
} |
||||
case SFL_CMD_FLASH: { |
||||
#if (defined CSR_SPIFLASH_BASE && defined SPIFLASH_PAGE_SIZE) |
||||
uint32_t addr; |
||||
|
||||
failed = 0; |
||||
addr = get_uint32(&frame.payload[0]); |
||||
|
||||
for (i = 4; i < frame.payload_length; i++) { |
||||
/* Erase page at sector boundaries before writing */ |
||||
if ((addr & (SPIFLASH_SECTOR_SIZE - 1)) == 0) { |
||||
erase_flash_sector(addr); |
||||
} |
||||
write_to_flash(addr, &frame.payload[i], 1); |
||||
addr++; |
||||
} |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
#endif |
||||
break; |
||||
} |
||||
case SFL_CMD_REBOOT: |
||||
#ifdef CSR_CTRL_RESET_ADDR |
||||
uart_write(SFL_ACK_SUCCESS); |
||||
ctrl_reset_write(1); |
||||
#endif |
||||
break; |
||||
default: |
||||
failed++; |
||||
if(failed == MAX_FAILED) { |
||||
printf("Too many consecutive errors, aborting"); |
||||
return 1; |
||||
} |
||||
uart_write(SFL_ACK_UNKNOWN); |
||||
break; |
||||
} |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* Ethernet Boot */ |
||||
/*-----------------------------------------------------------------------*/ |
||||
|
||||
#ifdef CSR_ETHMAC_BASE |
||||
|
||||
#ifndef LOCALIP1 |
||||
#define LOCALIP1 192 |
||||
#define LOCALIP2 168 |
||||
#define LOCALIP3 1 |
||||
#define LOCALIP4 20 // KQ |
||||
#endif |
||||
|
||||
#ifndef REMOTEIP1 |
||||
#define REMOTEIP1 192 |
||||
#define REMOTEIP2 168 |
||||
#define REMOTEIP3 1 |
||||
#define REMOTEIP4 11 // KQ |
||||
#endif |
||||
|
||||
#ifndef TFTP_SERVER_PORT |
||||
#define TFTP_SERVER_PORT 69 |
||||
#endif |
||||
|
||||
static const unsigned char macadr[6] = {0x10, 0xe2, 0xd5, 0x00, 0x00, 0x00}; |
||||
|
||||
static int copy_file_from_tftp_to_ram(unsigned int ip, unsigned short server_port, |
||||
const char *filename, char *buffer) |
||||
{ |
||||
int size; |
||||
printf("Copying %s to 0x%08x... ", filename, buffer); |
||||
size = tftp_get(ip, server_port, filename, buffer); |
||||
if(size > 0) |
||||
printf("(%d bytes)", size); |
||||
printf("\n"); |
||||
return size; |
||||
} |
||||
|
||||
static void netboot_from_json(const char * filename, unsigned int ip, unsigned short tftp_port) |
||||
{ |
||||
int size; |
||||
uint8_t i; |
||||
uint8_t count; |
||||
|
||||
/* FIXME: modify/increase if too limiting */ |
||||
char json_buffer[1024]; |
||||
char json_name[32]; |
||||
char json_value[32]; |
||||
|
||||
unsigned long boot_r1 = 0; |
||||
unsigned long boot_r2 = 0; |
||||
unsigned long boot_r3 = 0; |
||||
unsigned long boot_addr = 0; |
||||
|
||||
uint8_t image_found = 0; |
||||
uint8_t boot_addr_found = 0; |
||||
|
||||
/* Read JSON file */ |
||||
size = tftp_get(ip, tftp_port, filename, json_buffer); |
||||
if (size <= 0) |
||||
return; |
||||
|
||||
/* Parse JSON file */ |
||||
jsmntok_t t[32]; |
||||
jsmn_parser p; |
||||
jsmn_init(&p); |
||||
count = jsmn_parse(&p, json_buffer, strlen(json_buffer), t, sizeof(t)/sizeof(*t)); |
||||
for (i=0; i<count-1; i++) { |
||||
memset(json_name, 0, sizeof(json_name)); |
||||
memset(json_value, 0, sizeof(json_value)); |
||||
/* Elements are JSON strings with 1 children */ |
||||
if ((t[i].type == JSMN_STRING) && (t[i].size == 1)) { |
||||
/* Get Element's filename */ |
||||
memcpy(json_name, json_buffer + t[i].start, t[i].end - t[i].start); |
||||
/* Get Element's address */ |
||||
memcpy(json_value, json_buffer + t[i+1].start, t[i+1].end - t[i+1].start); |
||||
/* Skip bootargs (optional) */ |
||||
if (strncmp(json_name, "bootargs", 8) == 0) { |
||||
continue; |
||||
} |
||||
/* Get boot addr (optional) */ |
||||
else if (strncmp(json_name, "addr", 4) == 0) { |
||||
boot_addr = strtoul(json_value, NULL, 0); |
||||
boot_addr_found = 1; |
||||
} |
||||
/* Get boot r1 (optional) */ |
||||
else if (strncmp(json_name, "r1", 2) == 0) { |
||||
memcpy(json_name, json_buffer + t[i].start, t[i].end - t[i].start); |
||||
boot_r1 = strtoul(json_value, NULL, 0); |
||||
} |
||||
/* Get boot r2 (optional) */ |
||||
else if (strncmp(json_name, "r2", 2) == 0) { |
||||
boot_r2 = strtoul(json_value, NULL, 0); |
||||
} |
||||
/* Get boot r3 (optional) */ |
||||
else if (strncmp(json_name, "r3", 2) == 0) { |
||||
boot_r3 = strtoul(json_value, NULL, 0); |
||||
/* Copy Image from Network to address */ |
||||
} else { |
||||
size = copy_file_from_tftp_to_ram(ip, tftp_port, json_name, (void *)strtoul(json_value, NULL, 0)); |
||||
if (size <= 0) |
||||
return; |
||||
image_found = 1; |
||||
if (boot_addr_found == 0) /* Boot to last Image address if no bootargs.addr specified */ |
||||
boot_addr = strtoul(json_value, NULL, 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Boot */ |
||||
if (image_found) |
||||
boot(boot_r1, boot_r2, boot_r3, boot_addr); |
||||
} |
||||
|
||||
static void netboot_from_bin(const char * filename, unsigned int ip, unsigned short tftp_port) |
||||