Browse Source

projects/boot_stub: Import a multi-boot stub bitstream

This selects between several bitstream to be loaded at boot time.

Flash layout along with utility to create image with proper headers
for multiboot are included.

By default App 1 fw is started. If button is pressed at first boot, then
fw selection is possible (going to the 'DFU' mode image by default in
that case)

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 5 years ago
parent
commit
e1790c8ed2

+ 33 - 0
projects/boot_stub/Makefile

@@ -0,0 +1,33 @@
+# Project config
+PROJ = boot_stub
+
+PROJ_DEPS := misc
+PROJ_RTL_SRCS := $(addprefix rtl/, \
+)
+PROJ_TOP_SRC := rtl/top.v
+PROJ_TOP_MOD := top
+
+# Target config
+BOARD ?= icebreaker
+DEVICE = up5k
+PACKAGE = sg48
+
+NEXTPNR_ARGS = --freq 48 --no-promote-globals
+
+# Include default rules
+include ../../build/project-rules.mk
+
+# Custom rules
+$(BUILD_TMP)/boot_mb.bin: $(BUILD_TMP)/$(PROJ).bin
+	./utils/mkmultiboot.py $@ $<
+
+build-mb: $(BUILD_TMP)/boot_mb.bin
+
+prog-mb: $(BUILD_TMP)/boot_mb.bin
+	$(ICEPROG) $<
+
+sudo-prog-mb: $(BUILD_TMP)/boot_mb.bin
+	@echo 'Executing prog as root!!!'
+	sudo $(ICEPROG) $<
+
+.PHONY: build-mb prog-mb sudo-prog-mb

+ 18 - 0
projects/boot_stub/data/top-bitsy.pcf

@@ -0,0 +1,18 @@
+# USB
+set_io -nowarn usb_dp 43
+set_io -nowarn usb_dn 42
+set_io -nowarn usb_pu 38
+
+# Clock
+set_io -nowarn clk_in 35
+
+# Button
+set_io -nowarn btn 10
+
+# Leds
+set_io -nowarn rgb[0] 39
+set_io -nowarn rgb[1] 40
+set_io -nowarn rgb[2] 41
+
+set_io -nowarn ledr 11
+set_io -nowarn ledg 37

+ 18 - 0
projects/boot_stub/data/top-icebreaker.pcf

@@ -0,0 +1,18 @@
+# USB
+set_io -nowarn usb_dp 31
+set_io -nowarn usb_dn 34
+set_io -nowarn usb_pu 38
+
+# Clock
+set_io -nowarn clk_in 35
+
+# Button
+set_io -nowarn btn 10
+
+# Leds
+set_io -nowarn rgb[0] 39
+set_io -nowarn rgb[1] 40
+set_io -nowarn rgb[2] 41
+
+set_io -nowarn ledr 11
+set_io -nowarn ledg 37

+ 266 - 0
projects/boot_stub/rtl/top.v

@@ -0,0 +1,266 @@
+/*
+ * top.v
+ *
+ * Copyright (C) 2019  Sylvain Munaut <tnt@246tNt.com>
+ * All rights reserved.
+ *
+ * BSD 3-clause, see LICENSE.bsd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * vim: ts=4 sw=4
+ */
+
+`default_nettype none
+
+module top (
+	// Button
+	input  wire btn,
+
+	// LED
+	output wire ledr,
+	output wire ledg,
+
+	output wire [2:0] rgb,
+
+	// USB
+	output wire usb_dp,
+	output wire usb_dn,
+	output wire usb_pu,
+
+	// Clock
+	input  wire clk_in
+);
+
+	// FSM
+	// ---
+
+	localparam
+		ST_START	= 0,
+		ST_WAIT		= 1,
+		ST_SEL		= 2,
+		ST_BOOT		= 3;
+
+
+	// Signals
+	// -------
+
+	// Button input
+	wire btn_iob;
+	wire btn_v;
+	wire btn_r;
+	wire btn_f;
+
+	// FSM
+	reg  [1:0] state_nxt;
+	reg  [1:0] state;
+
+	// Boot selector
+	wire boot_now;
+	reg  [1:0] boot_sel;
+
+	// Timer/Counter
+	reg  [23:0] timer;
+	wire timer_tick;
+	wire timer_rst;
+
+	// LED
+	reg [3:0] dim;
+	wire [2:0] rgb_pwm;
+
+	// Clock / Reset
+	wire clk;
+	wire rst;
+
+
+	// Button
+	// ------
+
+	SB_IO #(
+		.PIN_TYPE(6'b000000),
+		.PULLUP(1'b1),
+		.IO_STANDARD("SB_LVCMOS")
+	) btn_iob_I (
+		.PACKAGE_PIN(btn),
+		.INPUT_CLK(clk),
+		.D_IN_0(btn_iob)
+	);
+
+	glitch_filter #(
+		.L(4),
+	) btn_flt_I (
+		.pin_iob_reg(btn_iob),
+		.cond(1'b1),
+		.val(btn_v),
+		.rise(btn_r),
+		.fall(btn_f),
+		.clk(clk),
+		.rst(1'b0)	// Ensure the glitch filter has settled
+					// before logic here engages
+	);
+
+
+	// State machine
+	// -------------
+
+	// Next state logic
+	always @(*)
+	begin
+		// Default is to stay put
+		state_nxt <= state;
+
+		// Main case
+		case (state)
+			ST_START:
+				// Check for immediate boot
+				state_nxt <= btn_v ? ST_BOOT : ST_WAIT;
+
+			ST_WAIT:
+				// Wait for first release
+				if (btn_v == 1'b1)
+					state_nxt <= ST_SEL;
+
+			ST_SEL:
+				// Wait for timeout
+				if (timer_tick)
+					state_nxt <= ST_BOOT;
+
+			ST_BOOT:
+				// Nothing to do ... will reconfigure shortly
+				state_nxt <= state;
+		endcase
+	end
+
+	// State register
+	always @(posedge clk or posedge rst)
+		if (rst)
+			state <= ST_START;
+		else
+			state <= state_nxt;
+
+
+	// Timer
+	// -----
+
+	always @(posedge clk)
+		if (timer_rst)
+			timer <= 24'h000000;
+		else
+			timer <= timer + 1;
+
+	assign timer_rst  = (btn_v == 1'b0) | timer_tick;
+	assign timer_tick = timer[23];
+
+
+	// Warm Boot
+	// ---------
+
+	// Boot command
+	assign boot_now = (state == ST_BOOT);
+
+	// Image select
+	always @(posedge clk or posedge rst)
+	begin
+		if (rst)
+			boot_sel <= 2'b10;	// App 1 Image by default
+		else if (state == ST_WAIT)
+			boot_sel <= 2'b01;	// DFU Image if in select mode
+		else if (state == ST_SEL)
+			boot_sel <= boot_sel + btn_f;
+	end
+
+	// IP
+	SB_WARMBOOT warmboot (
+		.BOOT(boot_now),
+		.S0(boot_sel[0]),
+		.S1(boot_sel[1])
+	);
+
+
+	// LED
+	// ---
+
+	assign rgb_pwm[0] = dim[3] & ~boot_now & boot_sel[0];
+	assign rgb_pwm[1] = dim[3] &  boot_now;
+	assign rgb_pwm[2] = dim[3] & ~boot_now & boot_sel[1];
+
+	assign ledr = ~boot_sel[0];
+	assign ledg = ~boot_sel[1];
+
+	// Dimming
+	always @(posedge clk)
+		if (rst)
+			dim <= 4'h0;
+		else
+			dim <= dim[3] ? 4'h0 : (dim + 1);
+
+	// Driver
+	SB_RGBA_DRV #(
+		.CURRENT_MODE("0b1"),
+		.RGB0_CURRENT("0b000001"),
+		.RGB1_CURRENT("0b000001"),
+		.RGB2_CURRENT("0b000001")
+	) rgb_drv_I (
+		.RGBLEDEN(1'b1),
+		.RGB0PWM(rgb_pwm[0]),
+		.RGB1PWM(rgb_pwm[1]),
+		.RGB2PWM(rgb_pwm[2]),
+		.CURREN(1'b1),
+		.RGB0(rgb[0]),
+		.RGB1(rgb[1]),
+		.RGB2(rgb[2])
+	);
+
+
+	// Dummy USB
+	// ---------
+	// (to avoid pullups triggering detection)
+
+	SB_IO #(
+		.PIN_TYPE(6'b101000),
+		.PULLUP(1'b0),
+		.IO_STANDARD("SB_LVCMOS")
+	) usb[2:0] (
+		.PACKAGE_PIN({usb_dp, usb_dn, usb_pu}),
+		.OUTPUT_ENABLE(1'b0),
+		.D_OUT_0(1'b0)
+	);
+
+
+	// Clock / Reset
+	// -------------
+
+	reg [7:0] cnt_reset;
+
+	SB_GB clk_gbuf_I (
+		.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_in),
+		.GLOBAL_BUFFER_OUTPUT(clk)
+	);
+
+	assign rst = ~cnt_reset[7];
+
+	always @(posedge clk)
+		if (cnt_reset[7] == 1'b0)
+			cnt_reset <= cnt_reset + 1;
+
+endmodule // top

+ 96 - 0
projects/boot_stub/utils/mkmultiboot.py

@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+
+import sys
+
+"""
+0		0x000000	Multiboot header
+16k     0x004000	Boot stub FPGA Image
+128k    0x020000	Boot stub Software image (not used)
+
+256k    0x040000	DFU FPGA Image
+384k    0x060000	DFU Software Image
+
+512k    0x080000	App 1 FPGA Image
+640k    0x0a0000	App 1 Software Image
+
+768k    0x0c0000	App 2 FPGA Image
+896k    0x0e0000	App 2 Software Image
+"""
+
+
+def hdr(mode, offset):
+	return bytes([
+		# Sync header
+		0x7e, 0xaa, 0x99, 0x7e,
+
+		# Boot mode
+		0x92, 0x00, (0x01 if mode else 0x00),
+
+		# Boot address
+		0x44, 0x03,
+			(offset >> 16) & 0xff,
+			(offset >>  8) & 0xff,
+			(offset >>  0) & 0xff,
+
+		# Bank offset
+		0x82, 0x00, 0x00,
+
+		# Reboot
+		0x01, 0x08,
+
+		# Padding
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	])
+
+
+offset_map = [
+	(True,  0x004000, 0x020000),
+	(False, 0x004000, 0x020000),
+	(False, 0x040000, 0x060000),
+	(False, 0x080000, 0x0a0000),
+	(False, 0x0c0000, 0x0e0000),
+]
+
+
+def load_image(img_name):
+	# Solit filename
+	if ':' in img_name:
+		bs_name, fw_name = img_name.split(':', 2)	
+		bs_name = bs_name or None
+		fw_name = fw_name or None
+	else:
+		bs_name = img_name
+		fw_name = None
+
+	# Read
+	bs = open(bs_name, 'rb').read() if (bs_name is not None) else b''
+	fw = open(fw_name, 'rb').read() if (fw_name is not None) else b''
+
+	return bs, fw
+
+	
+def main(argv0, out, *images):
+	# Build the header
+	mb_hdr = b''.join([hdr(m, o0) for m,o0,o1 in offset_map])
+
+	# Load images (if any)
+	images = [load_image(i) for i in images]
+
+	# Build final image
+	data = bytearray(mb_hdr)
+
+	for (_, o_bs, o_fw), (d_bs, d_fw) in zip(offset_map[1:], images):
+		for o, d in [(o_bs, d_bs), (o_fw, d_fw)]:
+			if len(d):
+				data[len(data):] = bytearray(o + len(d) - len(data))
+				data[o:o+len(d)] = d
+
+	# Write final image
+	with open(out, 'wb') as fh:
+		fh.write( data )
+
+
+if __name__ == '__main__':
+	main(*sys.argv)
+