/* * 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 = 1, cdone_state; uint32_t prom_addr, prom_sector_index; 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 mkm bitmap is stored right after the main bitstream and is aligned * on sector boundary * * 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); /* * first read the very first page of each sector to find the offset of * the mkm bitmap, skip the very first sector (there's no * sane way for the mkm bitmap may start there) * * then 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) */ for (prom_sector_index = 1; prom_sector_index < N25Q128_NUM_SECTORS; prom_sector_index += 1) { /* read next page */ prom_addr = prom_sector_index * N25Q128_SECTOR_SIZE; prom_ok = fpgacfg_read_data(prom_addr, prom_buf, N25Q128_PAGE_SIZE); if (prom_ok != HAL_OK) break; /* check magic marker */ prom_marker_bad = 0; for (c=0; c