From a781b2e15d97d227eee0dd4bdddf69e5e73d211e Mon Sep 17 00:00:00 2001 From: bunnie Date: Fri, 26 Apr 2024 18:16:12 +0800 Subject: [PATCH] start to lay in code rework for booting on real chips --- loader/src/platform/cramium/cramium.rs | 241 +++++++++++++++++++++---- 1 file changed, 203 insertions(+), 38 deletions(-) diff --git a/loader/src/platform/cramium/cramium.rs b/loader/src/platform/cramium/cramium.rs index ce8748a78..d4b03a5cb 100644 --- a/loader/src/platform/cramium/cramium.rs +++ b/loader/src/platform/cramium/cramium.rs @@ -2,8 +2,22 @@ use cramium_hal::iox::{Iox, IoxDir, IoxEnable, IoxFunction, IoxPort}; use cramium_hal::udma; use utralib::generated::*; +// Notes about the reset vector location +// This can be set using fuses in the IFR (also called 'info') region +// The offset is an 8-bit value, which is shifted into a final location +// according to the following formula: +// +// let short_offset: u8 = OFFSET; +// let phys_offset: u32 = 0x6000_0000 + short_offset << 14; +// +// The RV32-IV IFR fuse location is at row 6, byte 8. +// Each row is 256 bits wide. +// This puts the byte-address hex offset at (6 * 256 + 8 * 8) / 8 = 0xC8 +// within the IFR region. Total IFR region size is 0x200. + pub const RAM_SIZE: usize = utralib::generated::HW_SRAM_MEM_LEN; pub const RAM_BASE: usize = utralib::generated::HW_SRAM_MEM; +pub const FLASH_BASE: usize = utralib::generated::HW_RERAM_MEM; // Locate the hard-wired IFRAM allocations for UDMA pub const UART_IFRAM_ADDR: usize = utralib::HW_IFRAM0_MEM + utralib::HW_IFRAM0_MEM_LEN - 4096; @@ -14,7 +28,7 @@ pub const SPIM_RAM_IFRAM_ADDR: usize = utralib::HW_IFRAM0_MEM + utralib::HW_IFRA pub const SPIM_FLASH_IFRAM_ADDR: usize = utralib::HW_IFRAM0_MEM + utralib::HW_IFRAM0_MEM_LEN - 4 * 4096; // location of kernel, as offset from the base of ReRAM. This needs to match up with what is in link.x. -pub const KERNEL_OFFSET: usize = 0x28000; +pub const KERNEL_OFFSET: usize = 0x4_0000; #[cfg(feature = "cramium-soc")] pub fn early_init() { @@ -26,44 +40,78 @@ pub fn early_init() { // // Not all design changes have a rhyme or reason at this stage -- sometimes "it just works, // don't futz with it" is actually the answer that goes to production. - - /* - // "actual SoC" parameters -- swap the comment here when silicon comes back - // not making it a "feature" because this is a one-way gate, I don't see - // any reason why we'd go back to using the emulator board if we have silicon. + use utralib::utra::sysctrl; unsafe { - (0x400400a0 as *mut u32).write_volatile(0x1F598); // F - crate::println!("F: {:08x}", ((0x400400a0 as *const u32).read_volatile())); - let poke_array: [(u32, u32, bool); 12] = [ - (0x400400a4, 0x2812, false), // MN - (0x400400a8, 0x3301, false), // Q - (0x40040090, 0x0032, true), // setpll - (0x40040014, 0x7f7f, false), // fclk - (0x40040018, 0x7f7f, false), // aclk - (0x4004001c, 0x3f3f, false), // hclk - (0x40040020, 0x1f1f, false), // iclk - (0x40040024, 0x0f0f, false), // pclk - (0x40040010, 0x0001, false), // sel0 - (0x4004002c, 0x0032, true), // setcgu - (0x40040060, 0x0003, false), // aclk gates - (0x40040064, 0x0003, false), // hclk gates - ]; - for &(addr, dat, is_u32) in poke_array.iter() { - let rbk = if is_u32 { - (addr as *mut u32).write_volatile(dat); - (addr as *const u32).read_volatile() - } else { - (addr as *mut u16).write_volatile(dat as u16); - (addr as *const u16).read_volatile() as u32 - }; - if dat != rbk { - crate::println!("{:08x}(w) != {:08x}(r)", dat, rbk); - } else { - crate::println!("{:08x} ok", dat); + // this is MANDATORY for any chip stapbility in real silicon, as the initial + // clocks are too unstable to do anything otherwise. However, for the simulation + // environment, this can (should?) be dropped + let daric_cgu = sysctrl::HW_SYSCTRL_BASE as *mut u32; + daric_cgu.add(sysctrl::SFR_CGUSEL1.offset()).write_volatile(1); // 0: RC, 1: XTAL + daric_cgu.add(sysctrl::SFR_CGUFSCR.offset()).write_volatile(48); // external crystal is 48MHz + + daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); + + let duart = utra::duart::HW_DUART_BASE as *mut u32; + duart.add(utra::duart::SFR_CR.offset()).write_volatile(0); + duart.add(utra::duart::SFR_ETUC.offset()).write_volatile(24); + duart.add(utra::duart::SFR_CR.offset()).write_volatile(1); + } + // this block is mandatory in all cases to get clocks set into some consistent, expected mode + unsafe { + let daric_cgu = sysctrl::HW_SYSCTRL_BASE as *mut u32; + daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_0.offset()).write_volatile(0x7f7f); + daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_1.offset()).write_volatile(0x7f7f); + daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_2.offset()).write_volatile(0x3f3f); + daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_3.offset()).write_volatile(0x1f1f); + daric_cgu.add(utra::sysctrl::SFR_CGUFD_CFGFDCR_0_4_4.offset()).write_volatile(0x0f0f); + daric_cgu.add(utra::sysctrl::SFR_ACLKGR.offset()).write_volatile(0xFF); + daric_cgu.add(utra::sysctrl::SFR_HCLKGR.offset()).write_volatile(0xFF); + daric_cgu.add(utra::sysctrl::SFR_ICLKGR.offset()).write_volatile(0xFF); + daric_cgu.add(utra::sysctrl::SFR_PCLKGR.offset()).write_volatile(0xFF); + daric_cgu.add(utra::sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); + + let duart = utra::duart::HW_DUART_BASE as *mut u32; + // enable DUART + duart.add(utra::duart::SFR_CR.offset()).write_volatile(1); + } + // unsafe, direct-writes to address offsets are used here instead of the UTRA abstraction + // because there are some quirks in the early boot path that make the system more stable + // if all register accesses are in-lined. + #[cfg(feature = "boot-delay")] + unsafe { + // this block should immediately follow the CGU setup + let duart = utra::duart::HW_DUART_BASE as *mut u32; + // ~2 second delay for debugger to attach + let msg = b"boot\r"; + for j in 0..20_000 { + // variable count of .'s to create a sense of motion on the console + for _ in 0..j & 0x7 { + while duart.add(utra::duart::SFR_SR.offset()).read_volatile() != 0 {} + duart.add(utra::duart::SFR_TXD.offset()).write_volatile('.' as char as u32); + } + for &b in msg { + while duart.add(utra::duart::SFR_SR.offset()).read_volatile() != 0 {} + duart.add(utra::duart::SFR_TXD.offset()).write_volatile(b as char as u32); } } - } */ + } + #[cfg(feature = "sram-margin")] + unsafe { + // set SRAM delay to max - opens up timing margin as much a possible, supposedly? + let sram_ctl = utra::coresub_sramtrm::HW_CORESUB_SRAMTRM_BASE as *mut u32; + let waitcycles = 3; + sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM0.offset()).write_volatile( + (sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM0.offset()).read_volatile() & !0x18) + | ((waitcycles << 3) & 0x18), + ); + sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM1.offset()).write_volatile( + (sram_ctl.add(utra::coresub_sramtrm::SFR_SRAM1.offset()).read_volatile() & !0x18) + | ((waitcycles << 3) & 0x18), + ); + } // SoC emulator board parameters (deals with MMCM instead of PLL) + // Remove this once we feel confident we're sticking with SoC hardware. + /* unsafe { let poke_array: [(u32, u32, bool); 9] = [ (0x40040030, 0x0001, true), // cgusel1 @@ -90,7 +138,7 @@ pub fn early_init() { crate::println!("{:08x} ok", dat); } } - } + } */ // Configure the UDMA UART. This UART's settings will be used as the initial console UART. // This is configured in the loader so that the log crate does not have a dependency @@ -125,7 +173,7 @@ pub fn early_init() { ); let baudrate: u32 = 115200; - let freq: u32 = 100_000_000; + let freq: u32 = 45_882_000; // the address of the UART buffer is "hard-allocated" at an offset one page from the top of // IFRAM0. This is a convention that must be respected by the UDMA UART library implementation @@ -154,7 +202,7 @@ pub fn early_init() { // do a PL230/PIO test. Toggles PB15 (PIO0) with an LFSR sequence. let mut pl230 = xous_pl230::Pl230::new(); xous_pl230::pl230_tests::units::basic_tests(&mut pl230); - xous_pl230::pl230_tests::units::pio_test(&mut pl230); + // xous_pl230::pl230_tests::units::pio_test(&mut pl230); const BANNER: &'static str = "\n\rKeep pressing keys to continue boot...\r\n"; udma_uart.write(BANNER.as_bytes()); @@ -232,3 +280,120 @@ fn test_duart() { #[cfg(feature = "platform-tests")] pub fn platform_tests() { test_duart(); } + +pub unsafe fn init_clock_asic(freq_hz: u32) { + let daric_cgu = sysctrl::HW_SYSCTRL_BASE as *mut u32; + + const F_MHZ: u32 = 1_000_000; + const FREQ_0: u32 = 16 * F_MHZ; + + const TBL_Q: [u16; 7] = [ + // keep later DIV even number as possible + 0x7777, // 16-32 MHz + 0x7737, // 32-64 + 0x3733, // 64-128 + 0x3313, // 128-256 + 0x3311, // 256-512 // keep ~ 100MHz + 0x3301, // 512-1024 + 0x3301, /* 1024-1500 + * 0x1303, // 256-512 + * 0x0103, // 512-1024 + * 0x0001, // 1024-2048 */ + ]; + const TBL_MUL: [u32; 7] = [ + 64, // 16-32 MHz + 32, // 32-64 + 16, // 64-128 + 8, // 128-256 + 4, // 256-512 + 2, // 512-1024 + 2, // 1024-2048 + ]; + const M: u32 = 24 - 1; + + report_api(0xc0c0_0000); + let f16_mhz_log2 = (freq_hz / FREQ_0).ilog2() as usize; + report_api(f16_mhz_log2 as u32); + let n_fxp24: u64 = (((freq_hz as u64) << 24) * TBL_MUL[f16_mhz_log2] as u64) / (2 * F_MHZ as u64); + report_api(n_fxp24 as u32); + report_api((n_fxp24 >> 32) as u32); + let n_frac: u32 = (n_fxp24 & 0x00ffffff) as u32; + report_api(n_frac); + let pllmn = ((M << 12) & 0x0001F000) | ((n_fxp24 >> 24) & 0x00000fff) as u32; + report_api(pllmn); + let pllf = n_frac | (if 0 == n_frac { 0 } else { 1 << 24 }); + report_api(pllf); + let pllq = TBL_Q[f16_mhz_log2] as u32; + report_api(pllq); + + daric_cgu.add(sysctrl::SFR_CGUSEL1.offset()).write_volatile(1); // 0: RC, 1: XTAL + daric_cgu.add(sysctrl::SFR_CGUFSCR.offset()).write_volatile(48); // external crystal is 48MHz + daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); + + if freq_hz < 1_000_000 { + daric_cgu.add(sysctrl::SFR_IPCOSC.offset()).write_volatile(freq_hz); + daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32 + } + // switch to OSC + daric_cgu.add(sysctrl::SFR_CGUSEL0.offset()).write_volatile(0); // clktop sel, 0:clksys, 1:clkpll0 + daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); // commit + + if 0 == freq_hz { + // do nothing + } else { + // PD PLL + daric_cgu + .add(sysctrl::SFR_IPCLPEN.offset()) + .write_volatile(daric_cgu.add(sysctrl::SFR_IPCLPEN.offset()).read_volatile() | 0x02); + daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32 + + // delay + for _ in 0..1024 { + unsafe { core::arch::asm!("nop") }; + } + for _ in 0..4 { + report_api(0xc0c0_dddd); + } + + // printf ("%s(%4" PRIu32 "MHz) M = 24, N = %4lu.%08lu, Q = %2lu\n", + // __FUNCTION__, freqHz / 1000000, (uint32_t)(n_fxp24 >> + // 24).write_volatile((uint32_t)((uint64_t)(n_fxp24 & 0x00ffffff) * 100000000/(1UL + // <<24)).write_volatile(TBL_MUL[f16MHzLog2]); + daric_cgu.add(sysctrl::SFR_IPCPLLMN.offset()).write_volatile(pllmn); // 0x1F598; // ?? + daric_cgu.add(sysctrl::SFR_IPCPLLF.offset()).write_volatile(pllf); // ?? + daric_cgu.add(sysctrl::SFR_IPCPLLQ.offset()).write_volatile(pllq); // ?? TODO select DIV for VCO freq + + // VCO bias CPP bias CPI bias + // 1 2 3 + // DARIC_IPC->ipc = (3 << 6) | (5 << 3) | (5); + daric_cgu.add(sysctrl::SFR_IPCCR.offset()).write_volatile((1 << 6) | (2 << 3) | (3)); + daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32 + + daric_cgu + .add(sysctrl::SFR_IPCLPEN.offset()) + .write_volatile(daric_cgu.add(sysctrl::SFR_IPCLPEN.offset()).read_volatile() & !0x02); + daric_cgu.add(sysctrl::SFR_IPCARIPFLOW.offset()).write_volatile(0x32); // commit, must write 32 + + // delay + for _ in 0..1024 { + unsafe { core::arch::asm!("nop") }; + } + for _ in 0..4 { + report_api(0xc0c0_eeee); + } + // printf("read reg a0 : %08" PRIx32"\n", *((volatile uint32_t* )0x400400a0)); + // printf("read reg a4 : %04" PRIx16"\n", *((volatile uint16_t* )0x400400a4)); + // printf("read reg a8 : %04" PRIx16"\n", *((volatile uint16_t* )0x400400a8)); + + // TODO wait/poll lock status? + daric_cgu.add(sysctrl::SFR_CGUSEL0.offset()).write_volatile(1); // clktop sel, 0:clksys, 1:clkpll0 + daric_cgu.add(sysctrl::SFR_CGUSET.offset()).write_volatile(0x32); // commit + + report_api(0xc0c0_ffff); + // printf (" MN: 0x%05x, F: 0x%06x, Q: 0x%04x\n", + // DARIC_IPC->pll_mn, DARIC_IPC->pll_f, DARIC_IPC->pll_q); + // printf (" LPEN: 0x%01x, OSC: 0x%04x, BIAS: 0x%04x,\n", + // DARIC_IPC->lpen, DARIC_IPC->osc, DARIC_IPC->ipc); + } + report_api(0xc0c0_0007); +}