/*
* 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<sizeof prom_marker; c++)
if (prom_buf[c] != prom_marker[c]) {
prom_marker_bad = 1;
break;
}
/* mkm bitmap found! */
if (!prom_marker_bad) break;
}
/* check, that magic marker was correct */
if (prom_marker_bad) return 2;
/* send the mkm bitmap */
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;
/* 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 all the pages were written */
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;
}