aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel V. Shatov (Meister) <meisterpaul1@yandex.ru>2021-09-13 11:54:00 +0300
committerPavel V. Shatov (Meister) <meisterpaul1@yandex.ru>2021-09-13 11:54:00 +0300
commitc71f1f58e0d9ab8eae86604acd721c1e0abdcd3c (patch)
treeaa71d045c0b01cf5f21dad2d280bf9d2d675c1ef
parentd1624b391113f777f4aa9d70cc26ff6a9fe27ed2 (diff)
This adds two routines, ice40mkm_init() and ice40mkm_configure(). The former
should be called once during startup. The latter configures the iCE40 chip with a bitstream stored in the very end of the main FPGA's configuration memory. It should be called after startup and potentially after MKM bitstream is upgraded.
-rw-r--r--stm-ice40mkm.c201
-rw-r--r--stm-ice40mkm.h92
2 files changed, 293 insertions, 0 deletions
diff --git a/stm-ice40mkm.c b/stm-ice40mkm.c
new file mode 100644
index 0000000..5a03525
--- /dev/null
+++ b/stm-ice40mkm.c
@@ -0,0 +1,201 @@
+/*
+ * stm-ice40mkm.c
+ * --------------
+ * Functions for configuring the Lattice iCE40-based master key memory.
+ *
+ * Copyright 2021 The Commons Conservancy Cryptech Project
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * 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 copyright holder 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 THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS 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.
+ */
+
+#include "stm-ice40mkm.h"
+#include "stm-init.h"
+#include "stm-fpgacfg.h"
+
+/* handle to SPI3 */
+static SPI_HandleTypeDef hspi_ice40mkm;
+
+/*
+ * initialize SPI3 peripheral and assert reset signal
+ */
+void ice40mkm_init(void)
+{
+ /* initialize GPIO pins */
+ ICE40MKM_GPIO_INIT();
+
+ /*
+ * datasheet says up to 45 MHz is fine, but that's apparently for master mode
+ * slave speed not explicitly defined, but TN-02001-3.2 says only up to 25 MHz
+ * 4x prescaler is used for 22.5 MHz (2x works fine, but might be out of specs)
+ */
+
+ /* SPI3 (iCE40 slave SPI configuration interface) init function */
+ hspi_ice40mkm.Instance = SPI3;
+ hspi_ice40mkm.Init.Mode = SPI_MODE_MASTER;
+ hspi_ice40mkm.Init.Direction = SPI_DIRECTION_2LINES;
+ hspi_ice40mkm.Init.DataSize = SPI_DATASIZE_8BIT;
+ hspi_ice40mkm.Init.CLKPolarity = SPI_POLARITY_HIGH;
+ hspi_ice40mkm.Init.CLKPhase = SPI_PHASE_2EDGE;
+ hspi_ice40mkm.Init.NSS = SPI_NSS_SOFT;
+ hspi_ice40mkm.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
+ hspi_ice40mkm.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ hspi_ice40mkm.Init.TIMode = SPI_TIMODE_DISABLE;
+ hspi_ice40mkm.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ hspi_ice40mkm.Init.CRCPolynomial = 10;
+ HAL_SPI_Init(&hspi_ice40mkm);
+
+ /* assert iCE40 reset */
+ _ice40mkm_reset_assert();
+}
+
+/*
+ * force iCE40 into slave SPI mode
+ * read bitstream from the end of the main FPGA's config memory
+ * send the bitstream to iCE40
+ * check, that "config done" went high
+ *
+ * return codes:
+ * 0 = success
+ * 1 = serial transfer timeout
+ * 2 = bad magic marker (image missing?)
+ * 3 = config done did not go high (crc error, etc)
+ */
+int ice40mkm_configure(void)
+{
+ HAL_StatusTypeDef prom_ok;
+
+ size_t c;
+ uint8_t prom_buf[N25Q128_PAGE_SIZE];
+ uint8_t prom_marker[] = ICE40_MAGIC_MARKER;
+ int prom_marker_bad = 0, cdone_state;
+
+ uint32_t prom_addr, prom_offset = N25Q128_SECTOR_SIZE *
+ (N25Q128_NUM_SECTORS - ICE40_BITSTREAM_SECTORS);
+
+ uint32_t page, num_pages = ICE40_BITSTREAM_SECTORS *
+ (N25Q128_SECTOR_SIZE / N25Q128_PAGE_SIZE);
+
+ /*
+ * assuming ice40mkm_init() has already been called (reset
+ * has already been asserted), activate chip-select and
+ * de-assert reset to force device into slave mode, then
+ * wait for 2 ms to allow internal "housekeeping" to finish
+ */
+
+ _ice40mkm_chip_select(); //
+ HAL_Delay(1); // minimum reset duration is 200 ns
+ _ice40mkm_reset_deassert(); //
+ HAL_Delay(2); // minimum wait time is 1200 us
+
+ /*
+ * send 8 dummy clock ticks when chip select is high,
+ * then revert to low chip select
+ */
+ _ice40mkm_chip_deselect();
+ prom_ok = HAL_SPI_Transmit(&hspi_ice40mkm, prom_buf, 1, N25Q128_SPI_TIMEOUT);
+ if (prom_ok != HAL_OK) return 1;
+ _ice40mkm_chip_select();
+
+ /*
+ * device is now ready to receive the bitstream
+ *
+ * the bitstream is stored in the last two sectors of the main
+ * FPGA configuration memory
+ *
+ * the very first page of the bitstream starts with a magic marker,
+ * which can be used to detect whether the bitstream is present
+ */
+
+ /* claim access to FPGA config storage */
+ fpgacfg_access_control(ALLOW_ARM);
+
+ /*
+ * read the bitstream page-by-page and send it over SPI3
+ * note, that bitstream is slightly smaller than two sectors, so we're
+ * sending some 0xFF's after the binary image, which is harmless
+ * (just some dummy clock cycles)
+ */
+ prom_addr = prom_offset;
+ for (page=0; page<num_pages; page++)
+ {
+ /* read next page */
+ prom_ok = fpgacfg_read_data(prom_addr, prom_buf, N25Q128_PAGE_SIZE);
+ if (prom_ok != HAL_OK) break;
+
+ /* check magic marker */
+ if (page == 0) {
+ for (c=0; c<sizeof prom_marker; c++)
+ if (prom_buf[c] != prom_marker[c]) {
+ prom_marker_bad = 1;
+ break;
+ }
+ }
+
+ /* send page to iCE40 */
+ prom_ok = HAL_SPI_Transmit(&hspi_ice40mkm, prom_buf, N25Q128_PAGE_SIZE, N25Q128_SPI_TIMEOUT);
+ if (prom_ok != HAL_OK) break;
+
+ /* increment address */
+ prom_addr += N25Q128_PAGE_SIZE;
+ }
+
+ /* relinquish access to FPGA config storage */
+ fpgacfg_access_control(ALLOW_FPGA);
+
+ /* check, that magic marker was correct */
+ /* check, that all the pages were written */
+ if (prom_marker_bad) return 2;
+ else if (page != num_pages) return 1;
+
+ /* clear chip-select */
+ _ice40mkm_chip_deselect();
+
+ /*
+ * we should wait for at least 100 clock ticks before sampling
+ * the CDONE pin, we can only wait for multiples of 8, so let's
+ * wait for 128 (16 bytes) to be on the safe side
+ */
+ prom_ok = HAL_SPI_Transmit(&hspi_ice40mkm, prom_buf, 16, N25Q128_SPI_TIMEOUT);
+ if (prom_ok != HAL_OK) return 3;
+
+ /* now check config done pin */
+ cdone_state = _ice40mkm_cdone();
+ if (!cdone_state) return 3;
+
+ /*
+ * we should wait for at least 49 clock ticks after CDONE turns high to
+ * release SPI pins for user application, we're not using them anyway, but
+ * let's still follow vendor's recommendations just in case; we can only wait
+ * for multiples of 8, so let's wait for 64 ticks (8 bytes) instead of 49.
+ */
+ prom_ok = HAL_SPI_Transmit(&hspi_ice40mkm, prom_buf, 8, N25Q128_SPI_TIMEOUT);
+ if (prom_ok != HAL_OK) return 1;
+
+ /* cool, everything went fine, zero code means success */
+ return 0;
+}
diff --git a/stm-ice40mkm.h b/stm-ice40mkm.h
new file mode 100644
index 0000000..509e745
--- /dev/null
+++ b/stm-ice40mkm.h
@@ -0,0 +1,92 @@
+/*
+ * stm-ice40mkm.h
+ * --------------
+ * Functions for configuring the Lattice iCE40-based master key memory.
+ *
+ * Copyright 2021 The Commons Conservancy Cryptech Project
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * 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 copyright holder 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 THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS 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.
+ */
+
+
+#ifndef __STM32_ICE40MKM_H
+#define __STM32_ICE40MKM_H
+
+#include "stm32f4xx_hal.h"
+
+/* iCE40 UltraPlus 5K bitstream parameters */
+
+/*
+ * the bitstream itself is 104073 bytes
+ * iCEcube2 adds an extra trailing zero byte
+ * the bitstream fits in two 64K 256-page sectors
+ */
+#define ICE40_BITSTREAM_SECTORS 2
+
+/*
+ * the preamble of the bitstream
+ */
+#define ICE40_MAGIC_MARKER {0x7e, 0xaa, 0x99, 0x7e}
+
+
+/* Pins connected to the iCE40 */
+
+/* all pins are connected to I/O port C */
+#define ICE40MKM_Port GPIOC
+
+/* active-low reset input */
+#define ICE40MKM_CRESET_B_Pin GPIO_PIN_8
+
+/* config done output */
+#define ICE40MKM_CDONE_Pin GPIO_PIN_13
+
+/* active-low chip select */
+#define ICE40MKM_SPI_CS_N_Pin GPIO_PIN_9
+
+#define ICE40MKM_GPIO_INIT() \
+ __GPIOC_CLK_ENABLE(); \
+ gpio_output(ICE40MKM_Port, ICE40MKM_SPI_CS_N_Pin, GPIO_PIN_SET); \
+ gpio_output(ICE40MKM_Port, ICE40MKM_CRESET_B_Pin, GPIO_PIN_RESET); \
+ gpio_input(ICE40MKM_Port, ICE40MKM_CDONE_Pin, GPIO_PULLDOWN)
+
+/* macros to set/clear reset and chip-select */
+#define _ice40mkm_chip_select() {HAL_GPIO_WritePin(ICE40MKM_Port, ICE40MKM_SPI_CS_N_Pin, GPIO_PIN_RESET);}
+#define _ice40mkm_chip_deselect() {HAL_GPIO_WritePin(ICE40MKM_Port, ICE40MKM_SPI_CS_N_Pin, GPIO_PIN_SET);}
+
+#define _ice40mkm_reset_assert() {HAL_GPIO_WritePin(ICE40MKM_Port, ICE40MKM_CRESET_B_Pin, GPIO_PIN_RESET);}
+#define _ice40mkm_reset_deassert() {HAL_GPIO_WritePin(ICE40MKM_Port, ICE40MKM_CRESET_B_Pin, GPIO_PIN_SET);}
+
+/* macro to read config done output */
+#define _ice40mkm_cdone() (HAL_GPIO_ReadPin(ICE40MKM_Port, ICE40MKM_CDONE_Pin) == GPIO_PIN_SET ? 1 : 0)
+
+/* prototypes */
+extern void ice40mkm_init(void);
+extern int ice40mkm_configure(void);
+
+
+#endif /* __STM32_ICE40MKM_H */