From c71f1f58e0d9ab8eae86604acd721c1e0abdcd3c Mon Sep 17 00:00:00 2001 From: "Pavel V. Shatov (Meister)" Date: Mon, 13 Sep 2021 11:54:00 +0300 Subject: 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. --- stm-ice40mkm.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 stm-ice40mkm.c (limited to 'stm-ice40mkm.c') 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