From c3add0ce848f35c05db9bfbcb18107d18835acf4 Mon Sep 17 00:00:00 2001 From: Bernd Herzog Date: Wed, 14 Feb 2024 23:17:33 +0100 Subject: [PATCH] Cpld autodetect & boot splash screen (#1888) * added delayed error message when hackrf cpld initialization fails * refactoring * implemented portapack cpld autodetection * refactoring * fixed valid config range * added lcd fast setup * added boot splash screen * added one frame delay to remove flickering * fixed config persistence --- firmware/application/main.cpp | 6 +- firmware/application/portapack.cpp | 322 +++++++++++------- firmware/common/cpld_update.cpp | 124 ++++++- firmware/common/cpld_update.hpp | 4 +- firmware/common/lcd_ili9341.cpp | 30 ++ firmware/common/lcd_ili9341.hpp | 2 + .../common/portapack_persistent_memory.cpp | 6 +- 7 files changed, 357 insertions(+), 137 deletions(-) diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index fab21c7a..dd8f0f16 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -162,6 +162,8 @@ static void event_loop() { } int main(void) { + first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */ + if (config_mode_should_enter()) { config_mode_clear(); config_mode_run(); @@ -169,15 +171,13 @@ int main(void) { config_mode_set(); - first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */ - switch (portapack::init()) { case portapack::init_status_t::INIT_HACKRF_CPLD_FAILED: portapack::init_error = "HACKRF CPLD FAILED"; [[fallthrough]]; case portapack::init_status_t::INIT_SUCCESS: - portapack::display.init(); + config_mode_clear(); lcd_frame_sync_configure(); diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index 2293b4b4..7a88b57f 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -28,6 +28,7 @@ #include "hackrf_hal.hpp" #include "hackrf_gpio.hpp" using namespace hackrf::one; +#include "jtag_target_gpio.hpp" #include "clock_manager.hpp" #include "event_m0.hpp" @@ -50,6 +51,7 @@ using asahi_kasei::ak4951::AK4951; #include "file.hpp" #include "sd_card.hpp" #include "string_format.hpp" +#include "bitmap.hpp" namespace portapack { @@ -174,50 +176,21 @@ constexpr I2CConfig i2c_config_fast_clock{ enum class PortaPackModel { R1_20150901, R2_20170522, + AGM, + AUTODETECT, }; static bool save_config(int8_t value) { persistent_memory::set_config_cpld(value); - if (sd_card::status() == sd_card::Status::Mounted) { - ensure_directory("/hardware"); - File file; - auto sucess = file.create("/hardware/settings.txt"); - if (!sucess.is_valid()) { - file.write_line(to_string_dec_uint(value)); - } - } + portapack::persistent_memory::cache::persist(); return true; } -int read_file(std::string name) { - std::string return_string = ""; - File file; - auto success = file.open(name); - - if (!success.is_valid()) { - char one_char[1]; - for (size_t pointer = 0; pointer < file.size(); pointer++) { - file.seek(pointer); - file.read(one_char, 1); - return_string += one_char[0]; - } - return std::stoi(return_string); - } - return -1; -} - static int load_config() { static Optional config_value; if (!config_value.is_valid()) { int8_t value = portapack::persistent_memory::config_cpld(); - if ((value <= 0 || value >= 5) && sd_card::status() == sd_card::Status::Mounted) { - int data = read_file("/hardware/settings.txt"); - if (data != -1) { - config_value = data; - } - } else { - config_value = value; - } + config_value = value; } return config_value.value(); } @@ -226,39 +199,49 @@ static PortaPackModel portapack_model() { static Optional model; if (!model.is_valid()) { - const auto switches_state = get_switches_state(); - // Only save config if no other multi key boot action is triggered (like pmem reset) - if (switches_state.count() == 1) { - if (switches_state[(size_t)ui::KeyEvent::Up]) { + jtag::GPIOTarget target{ + portapack::gpio_cpld_tck, + portapack::gpio_cpld_tms, + portapack::gpio_cpld_tdi, + portapack::gpio_cpld_tdo}; + jtag::JTAG jtag{target}; + portapack::cpld::CPLD cpld{jtag}; + + cpld.reset(); + cpld.run_test_idle(); + uint32_t idcode = cpld.get_idcode(); + if (idcode == 0x25610) { + model = PortaPackModel::AGM; + } else { + const auto switches_state = swizzled_switches(); + // chDbgPanic(to_string_hex((uint32_t)switches_state.count(), 8).c_str()); + // Only save config if no other multi key boot action is triggered (like pmem reset) + if (((switches_state >> (size_t)ui::KeyEvent::Up) & 1) == 1) { save_config(1); // model = PortaPackModel::R2_20170522; // Commented these out as they should be set down below anyway - } else if (switches_state[(size_t)ui::KeyEvent::Down]) { + } else if (((switches_state >> (size_t)ui::KeyEvent::Down) & 1) == 1) { save_config(2); // model = PortaPackModel::R1_20150901; - } else if (switches_state[(size_t)ui::KeyEvent::Left]) { + } else if (((switches_state >> (size_t)ui::KeyEvent::Left) & 1) == 1) { save_config(3); // model = PortaPackModel::R1_20150901; - } else if (switches_state[(size_t)ui::KeyEvent::Right]) { + } else if (((switches_state >> (size_t)ui::KeyEvent::Right) & 1) == 1) { save_config(4); // model = PortaPackModel::R2_20170522; - } else if (switches_state[(size_t)ui::KeyEvent::Select]) { + } else if (((switches_state >> (size_t)ui::KeyEvent::Select) & 1) == 1) { save_config(0); } - } - if (load_config() == 1) { - model = PortaPackModel::R2_20170522; - } else if (load_config() == 2) { - model = PortaPackModel::R1_20150901; - } else if (load_config() == 3) { - model = PortaPackModel::R1_20150901; - } else if (load_config() == 4) { - model = PortaPackModel::R2_20170522; - } else { - if (audio_codec_wm8731.detected()) { - model = PortaPackModel::R1_20150901; // H1R1 + if (load_config() == 1) { + model = PortaPackModel::R2_20170522; + } else if (load_config() == 2) { + model = PortaPackModel::R1_20150901; + } else if (load_config() == 3) { + model = PortaPackModel::R1_20150901; + } else if (load_config() == 4) { + model = PortaPackModel::R2_20170522; } else { - model = PortaPackModel::R2_20170522; // H1R2, H2, H2+ + model = PortaPackModel::AUTODETECT; } } } @@ -345,6 +328,129 @@ static void shutdown_base() { clock_manager.shutdown(); } +static void set_cpu_clock_speed() { + /* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4 + * core clock speed to the 110 - 204MHz range. + */ + + /* Step into the 90-110MHz M4 clock range */ + /* OG: + * Fclkin = 40M + * /N=2 = 20M = PFDin + * Fcco = PFDin * (M=10) = 200M + * r9: + * Fclkin = 10M + * /N=1 = 10M = PFDin + * Fcco = PFDin * (M=20) = 200M + * Fclk = Fcco / (2*(P=1)) = 100M + */ + cgu::pll1::ctrl({ + .pd = 1, + .bypass = 0, + .fbsel = 0, + .direct = 0, + .psel = 0, + .autoblock = 1, + .nsel = hackrf_r9 ? 0UL : 1UL, + .msel = hackrf_r9 ? 19UL : 9UL, + .clk_sel = cgu::CLK_SEL::GP_CLKIN, + }); + + cgu::pll1::enable(); + while (!cgu::pll1::is_locked()) + ; + + set_clock_config(clock_config_pll1_step); + + /* Delay >50us at 90-110MHz clock speed */ + volatile uint32_t delay = 1400; + while (delay--) + ; + + set_clock_config(clock_config_pll1); + + /* Remove /2P divider from PLL1 output to achieve full speed */ + cgu::pll1::direct(); +} + +static void draw_splash_screen_icon(int16_t n, const ui::Bitmap& bitmap) { + ui::Painter painter; + + painter.draw_bitmap( + {portapack::display.width() / 2 - 8 - 40 + (n * 20), portapack::display.height() / 2 - 8 + 40}, + bitmap, + ui::Color::white(), + ui::Color::black()); +} + +static bool is_portapack_present() { + systime_t timeout = 50; + uint8_t wm8731_reset_command[] = {0x0f, 0x00}; + if (i2c0.transmit(0x1a /* wm8731 */, wm8731_reset_command, 2, timeout) == false) { + audio_codec_ak4951.reset(); + uint8_t ak4951_init_command[] = {0x00, 0x00}; + i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout); + chThdSleepMilliseconds(10); + if (i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout) == false) { + shutdown_base(); + return false; + } + } + + return true; +} + +static bool check_portapack_cpld() { + switch (portapack_model()) { + case PortaPackModel::AUTODETECT: { + portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_autodetect( + portapack::cpld::rev_20150901::config, portapack::cpld::rev_20170522::config); + if (result != portapack::cpld::CpldUpdateStatus::Success) { + shutdown_base(); + return false; + } + } break; + + case PortaPackModel::R1_20150901: + case PortaPackModel::R2_20170522: { + portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config()); + if (result == portapack::cpld::CpldUpdateStatus::Program_failed) { + chThdSleepMilliseconds(10); + // Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update + // Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update + if (load_config() != 3 /* left */ && load_config() != 4 /* right */) { + shutdown_base(); + return false; + } + } + } break; + + case PortaPackModel::AGM: + // the AGM devices are always factory flashed. so do nothing + break; + } + + return true; +} + +static void initialize_boot_splash_screen() { + ui::Painter painter; + portapack::display.init(); + + painter.fill_rectangle( + {0, 0, portapack::display.width(), portapack::display.height()}, + ui::Color::black()); + + chThdSleepMilliseconds(17); + portapack::backlight()->on(); + + painter.draw_bitmap( + {portapack::display.width() / 2 - 40, portapack::display.height() / 2 - 8}, + ui::bitmap_titlebar_image, + ui::Color::white(), + ui::Color::black()); +} + /* Clock scheme after exiting bootloader in SPIFI mode: * * XTAL_OSC = powered down @@ -396,23 +502,24 @@ init_status_t init() { i2c0.start(i2c_config_boot_clock); - // Keeping this here for now incase we need to revert - // if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) { - // shutdown_base(); - // return false; - // } - - // if( !hackrf::cpld::load_sram() ) { - // chSysHalt(); - // } chThdSleepMilliseconds(100); configure_pins_portapack(); portapack::io.init(); + persistent_memory::cache::init(); + + const auto switches_state = swizzled_switches() & (~(0xC0 | 0x80)); + bool lcd_fast_setup = switches_state == 0 && portapack::display.read_display_status(); + + if (lcd_fast_setup) { + initialize_boot_splash_screen(); + } else { + if (check_portapack_cpld() == false) + return init_status_t::INIT_PORTAPACK_CPLD_FAILED; + } /* Cache some configuration data from persistent memory. */ - persistent_memory::cache::init(); rtc_time::dst_init(); chThdSleepMilliseconds(10); @@ -425,48 +532,10 @@ init_status_t init() { set_clock_config(clock_config_irc); cgu::pll1::disable(); - /* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4 - * core clock speed to the 110 - 204MHz range. - */ + set_cpu_clock_speed(); - /* Step into the 90-110MHz M4 clock range */ - /* OG: - * Fclkin = 40M - * /N=2 = 20M = PFDin - * Fcco = PFDin * (M=10) = 200M - * r9: - * Fclkin = 10M - * /N=1 = 10M = PFDin - * Fcco = PFDin * (M=20) = 200M - * Fclk = Fcco / (2*(P=1)) = 100M - */ - cgu::pll1::ctrl({ - .pd = 1, - .bypass = 0, - .fbsel = 0, - .direct = 0, - .psel = 0, - .autoblock = 1, - .nsel = hackrf_r9 ? 0UL : 1UL, - .msel = hackrf_r9 ? 19UL : 9UL, - .clk_sel = cgu::CLK_SEL::GP_CLKIN, - }); - - cgu::pll1::enable(); - while (!cgu::pll1::is_locked()) - ; - - set_clock_config(clock_config_pll1_step); - - /* Delay >50us at 90-110MHz clock speed */ - volatile uint32_t delay = 1400; - while (delay--) - ; - - set_clock_config(clock_config_pll1); - - /* Remove /2P divider from PLL1 output to achieve full speed */ - cgu::pll1::direct(); + if (lcd_fast_setup) + draw_splash_screen_icon(0, ui::bitmap_icon_memory); usb_serial.initialize(); @@ -474,18 +543,11 @@ init_status_t init() { chThdSleepMilliseconds(10); /* Check if portapack is attached by checking if any of the two audio chips is present. */ - systime_t timeout = 50; - uint8_t wm8731_reset_command[] = {0x0f, 0x00}; - if (i2c0.transmit(0x1a /* wm8731 */, wm8731_reset_command, 2, timeout) == false) { - audio_codec_ak4951.reset(); - uint8_t ak4951_init_command[] = {0x00, 0x00}; - i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout); - chThdSleepMilliseconds(10); - if (i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout) == false) { - shutdown_base(); - return init_status_t::INIT_NO_PORTAPACK; - } - } + if (lcd_fast_setup == false && is_portapack_present() == false) + return init_status_t::INIT_NO_PORTAPACK; + + if (lcd_fast_setup) + draw_splash_screen_icon(1, ui::bitmap_icon_remote); touch::adc::init(); controls_init(); @@ -501,23 +563,20 @@ init_status_t init() { chThdSleepMilliseconds(10); - portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config()); - if (result == portapack::cpld::CpldUpdateStatus::Program_failed) { - chThdSleepMilliseconds(10); - // Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update - // Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update - if (load_config() != 3 /* left */ && load_config() != 4 /* right */) { - shutdown_base(); - return init_status_t::INIT_PORTAPACK_CPLD_FAILED; - } - } + if (lcd_fast_setup) + draw_splash_screen_icon(2, ui::bitmap_icon_sd); init_status_t return_code = init_status_t::INIT_SUCCESS; - if (!hackrf::cpld::load_sram()) { + if (lcd_fast_setup) + chDbgPanic("HACKRF CPLD FAILED"); + return_code = init_status_t::INIT_HACKRF_CPLD_FAILED; } + if (lcd_fast_setup) + draw_splash_screen_icon(3, ui::bitmap_icon_hackrf); + chThdSleepMilliseconds(10); // This delay seems to solve white noise audio issues LPC_CREG->DMAMUX = portapack::gpdma_mux; @@ -527,6 +586,13 @@ init_status_t init() { audio::init(portapack_audio_codec()); + if (lcd_fast_setup) + draw_splash_screen_icon(4, ui::bitmap_icon_speaker); + else { + portapack::display.init(); + portapack::backlight()->on(); + } + return return_code; } diff --git a/firmware/common/cpld_update.cpp b/firmware/common/cpld_update.cpp index 225754e1..b0d359aa 100644 --- a/firmware/common/cpld_update.cpp +++ b/firmware/common/cpld_update.cpp @@ -23,6 +23,7 @@ #include "hackrf_gpio.hpp" #include "portapack_hal.hpp" +#include "portapack.hpp" #include "jtag_target_gpio.hpp" #include "cpld_max5.hpp" @@ -30,11 +31,15 @@ #include "portapack_cpld_data.hpp" #include "hackrf_cpld_data.hpp" +#include "crc.hpp" + +#define REV_20150901_CHECKSUM 0xE0EF80FB +#define REV_20170522_CHECKSUM 0xD1BEB722 + namespace portapack { namespace cpld { -CpldUpdateStatus update_if_necessary( - const Config config) { +CpldUpdateStatus update_if_necessary(const Config config) { jtag::GPIOTarget target{ portapack::gpio_cpld_tck, portapack::gpio_cpld_tms, @@ -87,6 +92,121 @@ CpldUpdateStatus update_if_necessary( return ok ? CpldUpdateStatus::Success : CpldUpdateStatus::Program_failed; } +static CpldUpdateStatus enter_maintenance_mode(CPLD& cpld) { + /* Unknown state */ + cpld.reset(); + cpld.run_test_idle(); + + /* Run-Test/Idle */ + if (!cpld.idcode_ok()) { + return CpldUpdateStatus::Idcode_check_failed; + } + + cpld.sample(); + cpld.bypass(); + cpld.enable(); + + /* If silicon ID doesn't match, there's a serious problem. Leave CPLD + * in passive state. + */ + if (!cpld.silicon_id_ok()) { + return CpldUpdateStatus::Silicon_id_check_failed; + } + + return CpldUpdateStatus::Success; +} + +static void exit_maintenance_mode(CPLD& cpld) { + cpld.disable(); + cpld.bypass(); + + /* Initiate SRAM reload from flash we just programmed. */ + cpld.sample(); + cpld.clamp(); + cpld.disable(); +} + +static uint32_t get_firmware_crc(CPLD& cpld) { + CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff}; + cpld.prepare_read(0x0000); + + for (size_t i = 0; i < 3328; i++) { + uint16_t data = cpld.read(); + crc.process_byte((data >> 0) & 0xff); + crc.process_byte((data >> 8) & 0xff); + crc.process_byte((data >> 16) & 0xff); + crc.process_byte((data >> 24) & 0xff); + } + + cpld.prepare_read(0x0001); + + for (size_t i = 0; i < 512; i++) { + uint16_t data = cpld.read(); + crc.process_byte((data >> 0) & 0xff); + crc.process_byte((data >> 8) & 0xff); + crc.process_byte((data >> 16) & 0xff); + crc.process_byte((data >> 24) & 0xff); + } + + return crc.checksum(); +} + +CpldUpdateStatus update_autodetect(const Config config_rev_20150901, const Config config_rev_20170522) { + jtag::GPIOTarget target{ + portapack::gpio_cpld_tck, + portapack::gpio_cpld_tms, + portapack::gpio_cpld_tdi, + portapack::gpio_cpld_tdo}; + jtag::JTAG jtag{target}; + CPLD cpld{jtag}; + + if (portapack::display.read_display_status()) + return CpldUpdateStatus::Success; // LCD is ready + + CpldUpdateStatus result = enter_maintenance_mode(cpld); + if (result != CpldUpdateStatus::Success) + return result; + + uint32_t checksum = get_firmware_crc(cpld); + + if (checksum == REV_20170522_CHECKSUM) { + // H2 firmware present + if (!cpld.program(config_rev_20150901.block_0, config_rev_20150901.block_1)) + return CpldUpdateStatus::Program_failed; + } else if (checksum == REV_20150901_CHECKSUM) { + // H1 firmware present + if (!cpld.program(config_rev_20170522.block_0, config_rev_20170522.block_1)) + return CpldUpdateStatus::Program_failed; + } else { + // no firmware present + if (!cpld.program(config_rev_20150901.block_0, config_rev_20150901.block_1)) + return CpldUpdateStatus::Program_failed; + } + + exit_maintenance_mode(cpld); + + if (portapack::display.read_display_status()) + return CpldUpdateStatus::Success; // LCD is ready + + if (checksum != REV_20150901_CHECKSUM && checksum != REV_20170522_CHECKSUM) { + // try the other one + CpldUpdateStatus result = enter_maintenance_mode(cpld); + + if (result != CpldUpdateStatus::Success) + return result; + + if (!cpld.program(config_rev_20170522.block_0, config_rev_20170522.block_1)) + return CpldUpdateStatus::Program_failed; + + exit_maintenance_mode(cpld); + + if (portapack::display.read_display_status()) + return CpldUpdateStatus::Success; // LCD is ready + } + + return CpldUpdateStatus::Program_failed; +} + } /* namespace cpld */ } /* namespace portapack */ diff --git a/firmware/common/cpld_update.hpp b/firmware/common/cpld_update.hpp index a03d3cbd..09e9ba76 100644 --- a/firmware/common/cpld_update.hpp +++ b/firmware/common/cpld_update.hpp @@ -34,8 +34,8 @@ enum class CpldUpdateStatus { Program_failed = 3 }; -CpldUpdateStatus update_if_necessary( - const Config config); +CpldUpdateStatus update_if_necessary(const Config config); +CpldUpdateStatus update_autodetect(const Config config_rev_20150901, const Config config_rev_20170522); } /* namespace cpld */ } /* namespace portapack */ diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index 7196c191..7cd0035c 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -84,6 +84,17 @@ void lcd_wake() { lcd_display_on(); } +uint32_t lcd_read_display_status() { + io.lcd_data_write_command_and_data(0x09, {}); + io.lcd_read_word(); + + uint32_t value2 = io.lcd_read_word(); + uint32_t value3 = io.lcd_read_word(); + uint32_t value4 = io.lcd_read_word(); + uint32_t value5 = io.lcd_read_word(); + return value5 + (value4 << 8) + (value3 << 16) + (value2 << 24); +} + void lcd_init() { // LCDs are configured for IM[2:0] = 001 // 8080-I system, 16-bit parallel bus @@ -260,6 +271,25 @@ void lcd_vertical_scrolling_start_address( } // namespace +bool ILI9341::read_display_status() { + lcd_reset(); + uint32_t display_status = lcd_read_display_status(); + + /* This tries to validate the display_status. + * The value could vary from device to device, so we are less specific here. + * 0xFFFFFEFF was seen when the display was not reachable + * 0x00610000 was seen when the display was reachable + */ + + if (display_status > 0x0E000000ULL) + return false; + + if (display_status < 0x00000100ULL) + return false; + + return true; +} + void ILI9341::init() { lcd_reset(); lcd_init(); diff --git a/firmware/common/lcd_ili9341.hpp b/firmware/common/lcd_ili9341.hpp index 158121fd..20ea625b 100644 --- a/firmware/common/lcd_ili9341.hpp +++ b/firmware/common/lcd_ili9341.hpp @@ -42,6 +42,8 @@ class ILI9341 { ILI9341(ILI9341&&) = delete; void operator=(const ILI9341&) = delete; + bool read_display_status(); + void init(); void shutdown(); diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index dd73a5c9..ea7cf175 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -430,13 +430,15 @@ void defaults() { } void init() { - const auto switches_state = get_switches_state(); + const auto switches_state = swizzled_switches(); // ignore for valid check auto config_mode_backup = config_mode_storage_direct(); set_config_mode_storage_direct(CONFIG_MODE_NORMAL_VALUE); - if (!(switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Right]) && backup_ram->is_valid()) { + if (!(((switches_state >> (size_t)ui::KeyEvent::Left & 1) == 1) && + ((switches_state >> (size_t)ui::KeyEvent::Right & 1) == 1)) && + backup_ram->is_valid()) { // Copy valid persistent data into cache. cached_backup_ram = *backup_ram;