diff --git a/firmware/Makefile b/firmware/Makefile index d5c337e..ae71ab2 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -19,9 +19,14 @@ ifeq ($(APOLLO_BOARD), luna) endif ifeq ($(BOARD), cynthion_d11) - # These should default to the latest revision but can be set on the command line. - BOARD_REVISION_MAJOR ?= 1 - BOARD_REVISION_MINOR ?= 4 + # Enable revision autodetection by default if no revision is provided. + ifeq ($(BOARD_REVISION_MAJOR),) + ifeq ($(BOARD_REVISION_MINOR),) + BOARD_REVISION_DETECT := 1 + BOARD_REVISION_MAJOR := 255 + BOARD_REVISION_MINOR := 255 + endif + endif # On r0.1 or r0.2 boards, we target the SAMD21 configuration. ifeq ($(BOARD_REVISION_MAJOR), 0) @@ -36,6 +41,8 @@ else BOARD := $(APOLLO_BOARD) endif +# This should be set to 1 at this point if autodetection is enabled. +BOARD_REVISION_DETECT ?= 0 # Default to using a maximum possible HW version, which means "generic Apollo board". BOARD_REVISION_MAJOR ?= 255 @@ -61,6 +68,7 @@ CFLAGS += \ -Wno-unused-parameter \ -Wno-cast-qual \ -fstrict-volatile-bitfields \ + -D_BOARD_REVISION_DETECT_=$(BOARD_REVISION_DETECT) \ -D_BOARD_REVISION_MAJOR_=$(BOARD_REVISION_MAJOR) \ -D_BOARD_REVISION_MINOR_=$(BOARD_REVISION_MINOR) \ -D VERSION_STRING=\"$(VERSION_STRING)\" \ diff --git a/firmware/src/board_rev.c b/firmware/src/board_rev.c new file mode 100644 index 0000000..b0155e3 --- /dev/null +++ b/firmware/src/board_rev.c @@ -0,0 +1,25 @@ +/* + * Board revision detection. + * + * This file is part of Apollo. + * + * Copyright (c) 2024 Great Scott Gadgets + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "board_rev.h" + +/** + * Detect hardware revision using a board-specific method. + */ +__attribute__((weak)) void detect_hardware_revision(void) +{ +} + +/** + * Returns the board revision in bcdDevice format. + */ +__attribute__((weak)) uint16_t get_board_revision(void) +{ + return (_BOARD_REVISION_MAJOR_ << 8) | _BOARD_REVISION_MINOR_; +} diff --git a/firmware/src/board_rev.h b/firmware/src/board_rev.h new file mode 100644 index 0000000..063b648 --- /dev/null +++ b/firmware/src/board_rev.h @@ -0,0 +1,26 @@ +/* + * Board revision detection. + * + * This file is part of Apollo. + * + * Copyright (c) 2024 Great Scott Gadgets + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#ifndef __BOARD_REV_H__ +#define __BOARD_REV_H__ + +/** + * Detect hardware revision using a board-specific method. + */ +void detect_hardware_revision(void); + +/** + * Returns the board revision in bcdDevice format. + */ +uint16_t get_board_revision(void); + + +#endif diff --git a/firmware/src/boards/cynthion_d11/apollo_board.h b/firmware/src/boards/cynthion_d11/apollo_board.h index 40afcc2..f6e0692 100644 --- a/firmware/src/boards/cynthion_d11/apollo_board.h +++ b/firmware/src/boards/cynthion_d11/apollo_board.h @@ -52,7 +52,7 @@ enum { */ enum { FPGA_PROGRAM = PIN_PA08, -#if ((_BOARD_REVISION_MAJOR_ == 0) && (_BOARD_REVISION_MINOR_ < 6)) +#ifdef BOARD_HAS_SHARED_BUTTON PROGRAM_BUTTON = PIN_PA16, PHY_RESET = PIN_PA09, #else @@ -60,10 +60,24 @@ enum { USB_SWITCH = PIN_PA06, FPGA_ADV = PIN_PA09, #endif -#if ((_BOARD_REVISION_MAJOR_ == 1) && (_BOARD_REVISION_MINOR_ > 2)) + // FPGA sysCFG pins only in revs >= 1.3. FPGA_INITN = PIN_PA03, FPGA_DONE = PIN_PA04, -#endif +}; + + +/** + * Cynthion board revisions as bcdDevice values. + */ +enum { + CYNTHION_REV_UNKNOWN = 0, + CYNTHION_REV_0_6 = (0 << 8) | 6, + CYNTHION_REV_0_7 = (0 << 8) | 7, + CYNTHION_REV_1_0 = (1 << 8) | 0, + CYNTHION_REV_1_1 = (1 << 8) | 1, + CYNTHION_REV_1_2 = (1 << 8) | 2, + CYNTHION_REV_1_3 = (1 << 8) | 3, + CYNTHION_REV_1_4 = (1 << 8) | 4, }; diff --git a/firmware/src/boards/cynthion_d11/board_rev.c b/firmware/src/boards/cynthion_d11/board_rev.c new file mode 100644 index 0000000..93e71df --- /dev/null +++ b/firmware/src/boards/cynthion_d11/board_rev.c @@ -0,0 +1,108 @@ +/* + * Board revision detection for Cynthion. + * + * This file is part of Apollo. + * + * Copyright (c) 2024 Great Scott Gadgets + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include "apollo_board.h" +#include "board_rev.h" + +#include +#include +#include + + +#if (_BOARD_REVISION_DETECT_ == 1) + +static uint16_t revision = CYNTHION_REV_UNKNOWN; +static bool gsg_production = false; + +/** + * Detect hardware revision using Cynthion pin straps. + */ +void detect_hardware_revision(void) +{ + _pm_enable_bus_clock(PM_BUS_APBC, ADC); + _gclk_enable_channel(ADC_GCLK_ID, CONF_GCLK_ADC_SRC); + + // Initialize ADC device registers. + uint16_t calib_reg = ADC_CALIB_BIAS_CAL((*(uint32_t *)ADC_FUSES_BIASCAL_ADDR >> ADC_FUSES_BIASCAL_Pos)) + | ADC_CALIB_LINEARITY_CAL((*(uint64_t *)ADC_FUSES_LINEARITY_0_ADDR >> ADC_FUSES_LINEARITY_0_Pos)); + + hri_adc_wait_for_sync(ADC); + hri_adc_write_CTRLA_reg(ADC, ADC_CTRLA_SWRST); + hri_adc_wait_for_sync(ADC); + + hri_adc_write_CALIB_reg(ADC, calib_reg); + hri_adc_write_REFCTRL_reg(ADC, ADC_REFCTRL_REFCOMP | ADC_REFCTRL_REFSEL_INTVCC1); + hri_adc_write_CTRLB_reg(ADC, ADC_CTRLB_PRESCALER_DIV512 | ADC_CTRLB_RESSEL_12BIT); + hri_adc_write_INPUTCTRL_reg(ADC, ADC_INPUTCTRL_GAIN_DIV2 | ADC_INPUTCTRL_MUXPOS_PIN5 | ADC_INPUTCTRL_MUXNEG_GND); + hri_adc_write_CTRLA_reg(ADC, ADC_CTRLA_ENABLE); + + // Configure relevant GPIO to function as an ADC input. + gpio_set_pin_function(PIN_PA07, PINMUX_PA07B_ADC_AIN5); + + // Retrieve a single ADC reading. + hri_adc_set_SWTRIG_START_bit(ADC); + while (!hri_adc_get_interrupt_RESRDY_bit(ADC)); + uint16_t reading = hri_adc_read_RESULT_reg(ADC); + + // Convert ADC measurement to a percentage of the reference voltage. + uint32_t percentage = (((uint32_t)reading * 100) + 2048) >> 12; + if (percentage > 51) { + percentage = 100 - percentage; + gsg_production = true; + } + + /* + hardware version | percent of +3V3 + ___________________________________________ + 0.6 | 0-1 + future versions | 2-20 + 1.4 | 21-22 + 1.3 | 23-24 + 1.2 | 25-26 + 1.0 | 27-28 + 1.1 | 29-31 + reserved | 32-48 + 0.7 | 49-51 + reserved | 52-68 + 1.1-production | 69-71 + future-production | 72-100 + */ + + // Identify the board revision by comparing against expected thresholds. + struct { + uint16_t version; + uint8_t threshold; + } revisions[] = { + { CYNTHION_REV_0_6, 1 }, + { CYNTHION_REV_UNKNOWN, 20 }, + { CYNTHION_REV_1_4, 22 }, + { CYNTHION_REV_1_3, 24 }, + { CYNTHION_REV_1_2, 26 }, + { CYNTHION_REV_1_0, 28 }, + { CYNTHION_REV_1_1, 31 }, + { CYNTHION_REV_UNKNOWN, 48 }, + { CYNTHION_REV_0_7, 51 }, + }; + + int i = 0; + while (percentage > revisions[i].threshold) { ++i; } + revision = revisions[i].version; +} + +/** + * Returns the board revision in bcdDevice format. + */ +uint16_t get_board_revision(void) +{ + return revision; +} + +#endif diff --git a/firmware/src/boards/cynthion_d11/fpga.c b/firmware/src/boards/cynthion_d11/fpga.c index c649cc4..91395ee 100644 --- a/firmware/src/boards/cynthion_d11/fpga.c +++ b/firmware/src/boards/cynthion_d11/fpga.c @@ -11,6 +11,7 @@ #include "apollo_board.h" #include "jtag.h" #include "fpga.h" +#include "board_rev.h" /* @@ -20,17 +21,17 @@ */ void permit_fpga_configuration(bool enable) { -#if ((_BOARD_REVISION_MAJOR_ == 1) && (_BOARD_REVISION_MINOR_ > 2)) - gpio_set_pin_level(FPGA_INITN, enable); - gpio_set_pin_direction(FPGA_INITN, GPIO_DIRECTION_OUT); + if (get_board_revision() < CYNTHION_REV_1_3) return; - /* - * Delay a bit (in case the FPGA is already initializing) because - * TN-02039 says that PROGRAMN should not have a falling edge during - * initialization. - */ - board_delay(1); -#endif + gpio_set_pin_level(FPGA_INITN, enable); + gpio_set_pin_direction(FPGA_INITN, GPIO_DIRECTION_OUT); + + /* + * Delay a bit (in case the FPGA is already initializing) because + * TN-02039 says that PROGRAMN should not have a falling edge during + * initialization. + */ + board_delay(1); } diff --git a/firmware/src/main.c b/firmware/src/main.c index 8801b11..a5a6abd 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -32,6 +32,7 @@ #include #include +#include "board_rev.h" #include "led.h" #include "jtag.h" #include "fpga.h" @@ -49,6 +50,7 @@ int main(void) { board_init(); + detect_hardware_revision(); tusb_init(); fpga_io_init(); diff --git a/firmware/src/mcu/samd11/usb_descriptors.c b/firmware/src/mcu/samd11/usb_descriptors.c index ab4f515..186515b 100644 --- a/firmware/src/mcu/samd11/usb_descriptors.c +++ b/firmware/src/mcu/samd11/usb_descriptors.c @@ -26,13 +26,14 @@ */ #include "tusb.h" +#include "board_rev.h" #define SERIAL_NUMBER_STRING_INDEX 3 //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ -tusb_desc_device_t const desc_device = +tusb_desc_device_t desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, @@ -50,7 +51,6 @@ tusb_desc_device_t const desc_device = // These are a unique VID/PID for development LUNA boards. .idVendor = 0x1d50, .idProduct = 0x615c, - .bcdDevice = (_BOARD_REVISION_MAJOR_ << 8) | _BOARD_REVISION_MINOR_, .iManufacturer = 0x01, .iProduct = 0x02, @@ -63,6 +63,7 @@ tusb_desc_device_t const desc_device = // Application return pointer to descriptor uint8_t const * tud_descriptor_device_cb(void) { + desc_device.bcdDevice = get_board_revision(); return (uint8_t const *) &desc_device; }